简单介绍
dijkstra算法是图论中一个求单源最短路径的经典算法,通过逐步并入与当前已并入点相连接的最短路径的一个点,来找到起点和终点之间最短的路径。基本原理已经有很多博主阐述过了,不理解过程的安利去看天勤的数据结构这部分,很清楚。这里主要就c/c++语言的代码做一个详细讲解,代码亲测可以运行,从博主个人的理解出发,希望能帮到不理解代码过程的朋友!
代码讲解
1.数据结构及输入输出
这里采用图的邻接矩阵表示法:
typedef struct {
int v, e;//分别为点的数量、边的数量
int w[MAX][MAX];//权值
}Graph;
输入采用输入点和边的数量,在对边的信息进行输入,格式为“起点,终点,权值”
例如:表示一张五个点七条边的有向图
5 7
0 1 2
0 2 4
1 2 1
1 3 7
2 3 3
2 4 5
3 4 1
2.变量声明
我们需要定义一个dist[]数组,表示当前起点到其余各个顶点的最短路径,path[]数组存放记录的路径,存放的是前一个途经点,set[]数组表示当前并入的顶点集合,已经并入记为1。这里定义MAX和INF的值都为105。
3.dijkstra函数体
传入各个参数,v为起点。思路过程为:
(1)先初始化,对于从起点到其余每一个点的边进行遍历
①dist数组的初始值为图的初始值
②set[]数组初始值为0
③path数组,对于初始情况来说,如果这个边是通的,那说明最短路径这条路上的第一个点是起点,所以把v放入,如果这条边的长度为初始的INF,那说明起点到不了这里,没有上一个点,值赋为-1即可
(2)再纳入起点,set[v]=1
(3)再用一层for进行点数-1次循环处理剩余点
因为除了起点之外,还剩v-1个点,这块处理的思路是在与起点相连的点里面先找一个最短距离的点,作为扩展点。需要在外层for循环里面再套一层for循环。定义一个min最小值初始化为INF,再遍历dist数组,在这个点没有被访问过(set值不为1)且这个dist记录比最小值还小的时候,记录最小值,并记录最小值下标,就找到了下一个要被纳入的点的下标。
(4)纳入新点
(5)更新dist数组
这一步是最为关键的一步,新纳入点之后,当前起点到各个点的最短路径就可能发生变化,所以需要再次遍历一遍,当没有被访问过,且记录的路径比越过新的点的路径之和要小的时候,更新更小的那个值,dist[i]表示当前记录的从起点到i点最短路径,dist[u]表示从起点到u点(新并入的点)的最短路径,w[u][i]表示u点到i点这条边的权值,所以表示为代码就是dist[i] > dist[u] + g.w[u][i]
记录下那个最小值,说明走u点会使这条路更短,就把u放在path记录里面,更新path数组。
void dijkstra(int path[], int dist[],Graph g, int v) {
int set[MAX];
for (int i = 0; i < g.v; i++) {
dist[i] = g.w[v][i];//初始化dist数组
set[i] = 0;
if (g.w[v][i] < INF) {
//如果这个边可以走,那么把起点放在path路径上
path[i] = v;
}
else {
path[i] = -1;//否则说明它没有起点
}
}//初始化结束
set[v] = 1;//纳入起点
for (int i = 0; i < g.v - 1; i++) {
int min = INF; int u=0;
for (int j = 0; j < g.v; j++) {
//dist数组的含义为当前点到各个点的最短距离,当前初始化为起点到各个点
//在当前这组dist数中找最小的一个作为拓展
if (set[j] != 1 && dist[j] < min) {
min = dist[j];//找到当前最小值
u = j;//记录最小的下标
}
}
set[u]=1;//纳入最小值的点
for (int i = 0; i < g.v; i++) {
if (set[i] ==0 && dist[i] > dist[u] + g.w[u][i]) {
dist[i] = dist[u] + g.w[u][i];//看当前存入的从起点到当前点远还是从起点到刚刚并入的点+刚刚并入的点到其当前点远
path[i] = u;//更新path路径为上一个并入的点
}
}
}
}
4.traceback函数
这个函数用来回溯路径,因为path中记录了上一个点,所以不断递归调用即可,当值为-1时说明前面没有点了,到头了,return就行。
void traceback(int path[], int x, int y) {
//回溯从x点到y点的最短路径途径的顶点
if (path[y] == -1) {
return;//相当于不断的读取上一步的最短点是谁,当回溯到头的时候直接跳出
}
traceback(path, x, path[y]);//回溯上一步
cout << y<<endl;//打印点
}
5.全部代码展示
#define MAX 105
int INF = 105;
typedef struct {
int v, e;
int w[MAX][MAX];
}Graph;
void dijkstra(int path[], int dist[],Graph g, int v) {
int set[MAX];
for (int i = 0; i < g.v; i++) {
dist[i] = g.w[v][i];//初始化dist数组
set[i] = 0;
if (g.w[v][i] < INF) {
//如果这个边可以走,那么把起点放在path路径上
path[i] = v;
}
else {
path[i] = -1;//否则说明它没有起点
}
}//初始化结束
set[v] = 1;//纳入起点
for (int i = 0; i < g.v - 1; i++) {
int min = INF; int u=0;
for (int j = 0; j < g.v; j++) {
//dist数组的含义为当前点到各个点的最短距离,当前初始化为起点到各个点
//在当前这组dist数中找最小的一个作为拓展
if (set[j] != 1 && dist[j] < min) {
min = dist[j];//找到当前最小值
u = j;//记录最小的下标
}
}
set[u]=1;//纳入最小值的点
for (int i = 0; i < g.v; i++) {
if (set[i] ==0 && dist[i] > dist[u] + g.w[u][i]) {
dist[i] = dist[u] + g.w[u][i];//看当前存入的从起点到当前点远还是从起点到刚刚并入的点+刚刚并入的点到其当前点远
path[i] = u;//更新path路径为上一个并入的点
}
}
}
}
void traceback(int path[], int x, int y) {
//回溯从x点到y点的最短路径途径的顶点
if (path[y] == -1) {
return;//相当于不断的读取上一步的最短点是谁,当回溯到头的时候直接跳出
}
traceback(path, x, path[y]);//回溯上一步
cout << y<<endl;//打印点
}
int main() {
Graph g;
int path[MAX]; int dist[MAX];
int v, e; int a, b, c; int x, y;
cin >> v >> e;//输入点和边
g.v = v; g.e = e;
for (int i = 0; i < v; i++) {
for (int j = 0; j < v; j++) {
//初始化邻接矩阵
g.w[i][j] = INF;
}
}
for (int i = 0; i < e; i++) {
cin >> a >> b >> c;
g.w[a][b] = c;
}
dijkstra(path, dist, g, 0);//以0为起点
cout << "请输入查询点";
cin >> x >> y;
traceback(path, x, y);