1.概念
(1)最短路径:
- 非网图:两顶点之间经历边数最小的路径
- 网图:两顶点之间经历的边上权值之和最短的路
2.迪杰斯特拉(Dijkstra)算法
1.思路
设置一个集合S存放已经找到最短路径的顶点,并设置一个源点,dist[]数组中存放源点距离每个顶点的最短距离,path[]数组中存放的是最短路径,基本过程可以如下描述:(下图来自懒猫老师的《数据结构》相关课程笔记)
(1)先选定一个源点编号,将选定的源点加入U,将源点距(直接)其他顶点的距离赋值给dist[]数组
(2)选出现有dist[]中的最短路径,将该路径对应的顶点加入U中;并更新源点距其他顶点的距离值(意思是可以通过新加入的点再到达其他顶点)
(3)再重复(2)中步骤,直至所有顶点都加入U中
(4)所有结点加入U中,结束
2.数据结构
(1)图的存储结构:带权的邻接矩阵存储结构
(2)数组dist[n]:每个分量distfl表示当前所找到的从始点v到终点»的最短路径的长度。初态为:若从v到vi有弧,则disti为弧上权值;否则置distil为∞。
(3)数组path[n]:path[i]是一个字符串,表示当前所找到的从始点v到终点vi的最短路径。初态为:若从V有弧,则path[i]为0:否则置path[i]为-1。
(4)数组s[n]:存放源点和己经生成的终点,其初态为只有一个源点v
3.代码实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_VERTEX 10//最大的顶点个数
#define INT_MAX_ 100000
typedef int DataType;
int findMinDist(int *dist, int *s, int vertexNum);
void MGraph(DataType *vertex, int arc[][MAX_VERTEX], int vertexNum, int arcNum) { //初始化构造图(邻接矩阵法)
printf("请逐个输入顶点的内容:");
DataType x;
DataType vi, vj, ave; //构建邻接矩阵时,一条边的两个结点编号
for (int i = 0; i < vertexNum; i++) { //顶点数组赋值
scanf("%d", &x);
vertex[i] = x;
}
for (int i = 0; i < vertexNum; i++) //初始化邻接矩阵
for (int j = 0; j < vertexNum; j++) {
if (i == j)
arc[i][j] = 0;
else
arc[i][j] = INT_MAX_;//赋值正无穷
}
int count = 1;
for (int i = 0; i < arcNum; i++) { //依次输入每一条边
printf("请输入第%d条边依附的两个顶点的编号(方向->)和权值:", count++);
scanf("%d %d %d", &vi, &vj, &ave); //输入该边依附的顶点的编号
arc[vi][vj] = ave; //赋值权值
}
}
void Dijkstra(int arc[][MAX_VERTEX], int vertexNum, int startV, int *s, int *dist, int *path) {
for (int i = 0; i < vertexNum; i++) {
dist[i] = arc[startV][i]; //初始化数组dist,path
if (dist[i] != INT_MAX_)
path[i] = startV; //将原点设为上一条路径
else
path[i] = -1;
}
for (int i = 0; i < vertexNum; i++) {
s[i] = 0;
}
arr(dist, path, vertexNum);//验证数组内容是否正确
s[startV] = 1; //1值代表该结点已经加入了最短路径
int num = 1;
int min;
while (num < vertexNum) { //当顶点数num小于图的顶点数,即不是所有顶点加入最小路径
min = findMinDist(dist, s, vertexNum); //dist中查找s[i]为0的最小值元素
s[min] = 1;
//将新生成的结点加入集合s中
for (int i = 0; i < vertexNum; i++) {
//修改数组dist和path
if ((s[i] == 0) && (dist[i] > dist[min] + arc[min][i])) {
dist[i] = dist[min] + arc[min][i]; //用已经找到的最短路径修改对应的dist
path[i] = min; //用已经找到的最短路径修改对应的path
arr(dist, path, vertexNum);//验证数组内容是否正确
printf("\n");
}
}
num++;
}
}
int findMinDist(int *dist, int *s, int vertexNum) {
int flag = 0, min, index;
for (int i = 0; i < vertexNum; i++) {
if (flag == 0 && s[i] == 0) {
min = dist[i];
index = i;
flag = 1;
} else if (flag == 1 && s[i] == 0 && min > dist[i]) {
min = dist[i];
index = i;
}
}
return index;
}
void displayPath(int *dist, int *path, int *s, int startV, int vertexNum) { //打印起始点到各顶点的最短路径
int temp;
int patharr[vertexNum];
int con = 0;
for (int i = 0; i < vertexNum; i++) {
con = 0;
if (i != startV) {
printf("从顶点 %d --> %d:\n", startV, i);
if (dist[i] != INT_MAX_)
printf("最短路径长度为:%d\n", dist[i]);
else {
printf("不存在与该点之间的路径!\n");
printf("\n");
continue;
}
printf("最短路径为:");
temp = i;
while (temp != startV) { //得改成逆序的
patharr[con++] = path[temp];
temp = path[temp];
}
con--;
if (con == 0) {
printf("%d ->%d\n", patharr[con], i);
} else {
while (con >= 0) {
printf("%d -> ", patharr[con]);
con--;
}
printf("%d\n", i);
}
printf("\n");
}
}
}
void printMGraph(DataType *vertex, int arc[][MAX_VERTEX], int vertexNum) { //输出
printf("vertex:");
for (int i = 0; i < vertexNum; i++) {
printf("%d ", vertex[i]);
}
printf("\n");
printf("arc:\n");
for (int i = 0; i < vertexNum; i++) {
for (int j = 0; j < vertexNum; j++) {
if (j == vertexNum - 1) {
if (arc[i][j] == INT_MAX_)
printf(" *\n");
else
printf(" %d\n", arc[i][j]);
} else {
if (arc[i][j] == INT_MAX_)
printf(" * ");
else
printf(" %d ", arc[i][j]);
}
}
}
}
main() {
DataType vertex[MAX_VERTEX];//储存所有的顶点
int arc[MAX_VERTEX][MAX_VERTEX];//邻接矩阵,结点间的连通关系
int vertexNum, arcNum; //顶点个数,边的个数
printf("请输入顶点个数和边的个数:");
scanf("%d %d", &vertexNum, &arcNum);
MGraph(vertex, arc, vertexNum, arcNum);
printf("输出邻接矩阵信息:\n");
printMGraph(vertex, arc, vertexNum);
int x;
printf("请输入Dijkstra算法的起点:");
scanf("%d", &x);
int s[vertexNum], dist[vertexNum], path[vertexNum];
Dijkstra(arc, vertexNum, x, s, dist, path);
printf("\n");
printf("逐个输出由起点 %d 到所有顶点的最短路径:\n", x);
printf("\n");
displayPath(dist, path, s, x, vertexNum);
}
void arr(int *dist, int *path, int vertexNum) {//验证输出数组,用来debug
printf("检查数组内容:\n");
for (int i = 0; i < vertexNum; i++) {
printf("%d ", dist[i]);
}
printf("\n");
for (int i = 0; i < vertexNum; i++) {
printf("%d ", path[i]);
}
printf("\n");
}
4.输出测试
示例图:
(两组,分别以0和1为最短路径寻找的起点)
(1)以0为起点
(1)以1为起点
初学小白,有错误的话欢迎指正!