上一篇简单的介绍了图的应用之最小生成树的两种求解方法,本篇来了解一下关于 图的最短路径 的两种求解方法
首先看下面的图,
从上图中可以很直观的看出:
最短路径:V0 -> V1 -> V2 -> V4 -> V3 -> V6 -> V7 -> V8
最短路径权值和: 1 + 3 + 1 + 2 + 3 + 2 + 4 = 16
那么最短路径怎么求解呢?接下来Dijkstra算法
首先,我们可以将上面的图,存储到 邻接矩阵 中,如下:
因为是无向图,所以是对称的
我们知道,从 V0
到 V1
的权重是 1
,V0
到V2
的权重是5
,V1
到V2
的权重是3
,
那么,可以先从V0
到 V1
,再从V1
到 V2
,总权重是4
,优于直接从 V0
到 V2
,
因此,我们可以修改V0
到V2
的权重为4
,路径为 V0 -> V1 -> V2
,但是我们并不能直接修改 邻接矩阵,
所以,可以定义一个 数组D,来表示V0
到某个顶点 Vw
的路径。
定义一个数组final,来标识V0
到Vw
直接是否有最短路径,数组final[w] = 1
,表示V0
到Vw
有最短路径
定义一个 数组P,来记录当前顶点
的前驱顶点
的下标
。
思路:
1. 定义 数组D 表示V0 到Vw 的路径,数组final 表示V0 到Vw 是否求得最短路径(1表示已经求得)
,数组P 表示当前顶点的前驱顶点的下标(最后根据 P 数组,打印出V0到 Vw 的路径)
2. 初始化三个数组:
数组D:V0 和其他顶点的路径
数组P:先默认所有顶点的前驱都是V0(然后遍历修改前驱的下标)
数组final: 未知最短路径状态 0
3. V0 到 V0的路径为0,D[0] = 0, V0 到 V0的没有路径,final[v0] = 1,
P[V0] = -1(-1表示没有路径)
4. 开始循环,找到离 V0 最近的顶点,即从 数组D 中找到距离V0权重最小的顶点的下标 k,并记录最小权重
5. 将目前找到最近的顶点,标记为1,表示到V0有最小路径
6. 在找到V0到Vk最短路径的基础上,对于 Vk 与其他顶点的边进行计算,当其小于 V0 直接到某顶点的距离且该顶点未被标记为有最小路径时,更新V0到某顶点的距离,得到V0与它们的当前最短距离。
代码实现:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
#define INFINITYC 65535
typedef int Status;
typedef struct {
int vexs[MAXVEX];
int arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
}MGraph;
//存储最短路径下标的数组
typedef int Patharc[MAXVEX];
//存储到各点最短路径权值的和
typedef int ShortPathTable[MAXVEX];
void CreateMGraph(MGraph *G)
{
int i, j;
G->numEdges=16;
G->numVertexes=9;
for (i = 0; i < G->numVertexes; i++)
{
G->vexs[i]=i;
}
for (i = 0; i < G->numVertexes; i++)
{
for ( j = 0; j < G->numVertexes; j++)
{
if (i==j)
G->arc[i][j]=0;
else
G->arc[i][j] = G->arc[j][i] = INFINITYC;
}
}
G->arc[0][1]=1;
G->arc[0][2]=5;
G->arc[1][2]=3;
G->arc[1][3]=7;
G->arc[1][4]=5;
G->arc[2][4]=1;
G->arc[2][5]=7;
G->arc[3][4]=2;
G->arc[3][6]=3;
G->arc[4][5]=3;
G->arc[4][6]=6;
G->arc[4][7]=9;
G->arc[5][7]=5;
G->arc[6][7]=2;
G->arc[6][8]=7;
G->arc[7][8]=4;
for(i = 0; i < G->numVertexes; i++)
{
for(j = i; j < G->numVertexes; j++)
{
G->arc[j][i] =G->arc[i][j];
}
}
}
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *P, ShortPathTable *D) {
int v,w,k,min = 0;
k = 0;
// 定义数组,标记到V0是否有最短路径,1有最短路径,0为未知d最短路径
int final[MAXVEX];
// 初始化
for (v = 0; v < G.numVertexes; v++) {
// 默认未知最短路径状态 0,
final[v] = 0;
// 将 V0 与其他有连接的顶点的路径赋值给D
(*D)[v] = G.arc[0][v];
//初始化路径数组p = 0;
(*P)[v] = 0;
}
// V0到V0 是没有路径1
final[v0] = 1;
// 没有前驱,前驱下标为-1,
(*P)[v0] = -1;
// V0 到 V0 路径为0,
(*D)[v0] = 0;
// ✅开始主循环
for (v = 1; v < G.numVertexes; v++) {
min = INFINITYC;
// ✅寻找离V0最近的顶点
for (w = 0; w < G.numVertexes; w++) {
if (!final[w] && (*D)[w] < min) {
// 找到,修改最小值,并记录最小顶点下标
k = w;
min = (*D)[w];
}
}
// ✅修改标记,标识下标为k的顶点到V0有最小路径
final[k] = 1;
// ✅在V0到Vk的基础上,对于Vk到其他顶点的边进行计算,
for (w = 0; w < G.numVertexes; w++) {
// ✅V0 到Vk + Vk到Vw 的权重 < V0 直接到Vw的路径,更新V0 直接到Vw的路径
// 并记录顶点Vw的前驱顶点下标
if (!final[w] && (min + G.arc[k][w] < (*D)[w])) {
(*D)[w] = min + G.arc[k][w];
(*P)[w] = k;
}
}
}
}
// 调用
void show() {
int i,j,v0;
MGraph G;
Patharc P;
ShortPathTable D;
v0=0;
CreateMGraph(&G);
ShortestPath_Dijkstra(G, v0, &P, &D);
printf("最短路径路线:\n");
for(i=1;i v%d : ",v0,i);
j=i;
while(P[j]!=-1)
{
printf("%d ",P[j]);
j=P[j];
}
printf("\n");
}
printf("\n最短路径权值和\n");
for(i=1;i v%d : %d \n",G.vexs[0],G.vexs[i],D[i]);
printf("\n");
}
执行流程分析:
定义并初始化数组如下:
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
数组 D:(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 5 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ |
数组 P:(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
第一次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 5 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 8 | 6 | ∞ | ∞ | ∞ | ∞ |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
先找到最小下标k = 1, min = 1,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 0,G.arc[1][2] = 3,min + G.arc[1][2] < D[2](1 + 3 < 5),
满足,找到V0 -> V2最短路径,更新D[2] = 4, 更新顶点 V2 前驱顶点的下标,P[2] = 1
4. w = 3,final[3] = 0,G.arc[1][3] = 7,min + G.arc[1][3] < D[3](1 + 7 < ∞),
满足,找到V0 -> V3最短路径,更新D[3] = 8, 更新顶点 V3 前驱顶点的下标,P[3] = 1
5. w = 4,final[4] = 0,G.arc[1][4] = 5,min + G.arc[1][4] < D[4](1 + 5 < ∞),
满足,找到V0 -> V4最短路径,更新D[4] = 6, 更新顶点 V4 前驱顶点的下标,P[4] = 1
6. w = 5, final[5] = 0,G.arc[1][1] = 0,min + G.arc[1][5] < D[5](1 + ∞ < ∞),不满足条件
7. 同上,当w = 6,w = 7,w = 8时,条件不成立
第二次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 8 | 6 | ∞ | ∞ | ∞ | ∞ |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 8 | 5 | 11 | ∞ | ∞ | ∞ |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 1 | 2 | 2 | 0 | 0 | 0 |
先找到最小下标k = 2, min = 4,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 0,G.arc[2][3] = ∞,min + G.arc[2][3] < D[3](4 + ∞ < 8),
条件不成立
5. w = 4,final[4] = 0,G.arc[2][4] = 1,min + G.arc[2][4] < D[4](4 + 1 < 6),
满足,找到V0 -> V4最短路径,更新D[4] = 5, 更新顶点 V4 前驱顶点的下标,P[4] = 2
6. w = 5, final[5] = 0,G.arc[2][5] = 7,min + G.arc[1][5] < D[5](4 + 7 < ∞),
满足,找到V0 -> V5最短路径,更新D[5] = 11, 更新顶点 V5 前驱顶点的下标,P[5] = 2
7. w = 6,final[6] = 0,G.arc[2][6] = ∞,min + G.arc[2][6] < D[3](4 + ∞ < ∞),条件不成立
8. 同上,当w = 7,w = 8时,条件不成立
第三次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 8 | 5 | 11 | ∞ | ∞ | ∞ |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 11 | 14 | ∞ |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 1 | 2 | 2 | 0 | 0 | 0 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 4 | 4 | 0 |
先找到最小下标k = 4, min = 5,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 0,G.arc[4][3] = 2,min + G.arc[4][3] < D[3](5 + 2 < 8),
满足,找到V0 -> V3最短路径,更新D[3] = 7, 更新顶点 V3 前驱顶点的下标,P[3] = 4
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 0,G.arc[4][5] = 3,min + G.arc[4][5] < D[5](5 + 3 < 11),
满足,找到V0 -> V5最短路径,更新D[5] = 8, 更新顶点 V5 前驱顶点的下标,P[5] = 4
7. w = 6,final[6] = 0,G.arc[4][6] = 6,min + G.arc[4][6] < D[6](5 + 6 < ∞),
满足,找到V0 -> V6最短路径,更新D[6] = 11, 更新顶点 V6 前驱顶点的下标,P[6] = 4
8. w = 7,final[7] = 0,G.arc[4][7] = 9,min + G.arc[4][7] < D[7](5 + 9 < ∞),
满足,找到V0 -> V7最短路径,更新D[7] = 14, 更新顶点 V7 前驱顶点的下标,P[7] = 4
9. w = 8,final[8] = 0,G.arc[4][8] = ∞,min + G.arc[4][8] < D[7](5 + ∞ < ∞),不满足条件
第四次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 11 | 14 | ∞ |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 14 | ∞ |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 4 | 4 | 0 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 4 | 0 |
先找到最小下标k = 3, min = 7,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 0,G.arc[3][5] = ∞,min + G.arc[3][5] < D[5](7 + ∞ < 8),不满足条件
7. w = 6,final[6] = 0,G.arc[3][6] = 3,min + G.arc[3][6] < D[6](7 + 3 < 11),
满足,找到V0 -> V6最短路径,更新D[6] = 10, 更新顶点 V6 前驱顶点的下标,P[6] = 3
8. w = 7,final[7] = 0,G.arc[3][7] = ∞,min + G.arc[3][7] < D[7](7 + ∞ < 14),不满足条件
9. w = 8,final[8] = 0,G.arc[3][8] = ∞,min + G.arc[3][8] < D[7](7 + ∞ < ∞),不满足条件
第五次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 14 | ∞ |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 13 | ∞ |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 4 | 0 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 5 | 0 |
先找到最小下标k = 5, min = 8,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 0,G.arc[5][6] = ∞,min + G.arc[3][6] < D[6](8 + ∞ < 11),不满足条件
8. w = 7,final[7] = 0,G.arc[5][7] = 5,min + G.arc[3][7] < D[7](8 + 5 < 14),
满足,找到V0 -> V7最短路径,更新D[7] = 13, 更新顶点 V7 前驱顶点的下标,P[7] = 5
9. w = 8,final[8] = 0,G.arc[5][8] = ∞,min + G.arc[3][8] < D[7](8 + ∞ < ∞),不满足条件
第六次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 13 | ∞ |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 12 | 17 |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 5 | 0 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 6 | 6 |
先找到最小下标k = 6, min = 10,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 1,不满足条件
8. w = 7,final[7] = 0,G.arc[6][7] = 2,min + G.arc[6][7] < D[7](10 + 2 < 13),
满足,找到V0 -> V7最短路径,更新D[7] = 12, 更新顶点 V7 前驱顶点的下标,P[7] = 6
9. w = 8,final[8] = 0,G.arc[6][8] = 7,min + G.arc[6][8] < D[7](10 + 7 < ∞),足条件
满足,找到V0 -> V8最短路径,更新D[8] = 17, 更新顶点 V8 前驱顶点的下标,P[8] = 6
第七次执行主循环
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
数组 D(旧):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 12 | 17 |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 12 | 16 |
数组 P(旧):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 6 | 6 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 6 | 7 |
先找到最小下标k = 7, min = 12,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 1,不满足条件
8. w = 7,final[7] = 1,不满足条件
9. w = 8,final[8] = 0,G.arc[7][8] = 4,min + G.arc[7][8] < D[7](12 + 4 < 17),足条件
满足,找到V0 -> V8最短路径,更新D[8] = 16, 更新顶点 V8 前驱顶点的下标,P[8] = 7
第八次执行主循环
先找到最小下标k = 8, min = 16,设置final[8] = 1,如下,所以不会在更新数组D 和数组P,最终得到数组P
final 数组:(标记V0
和其他顶点是否有最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
数组 D(新):(V0
到某个顶点Vw
的最短路径)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
0 | 1 | 4 | 7 | 5 | 8 | 10 | 12 | 16 |
数组 P(新):(当前顶点的前驱的下标)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
-1 | 0 | 1 | 4 | 2 | 4 | 3 | 6 | 7 |
先找到最小下标k = 7, min = 12,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 1,不满足条件
8. w = 7,final[7] = 1,不满足条件
9. w = 8,final[8] = 1,不满足条件
最后根据数组P和数组D,求得V0
到某个顶点的最短路径和权重,
在求最短路径时,遍历数组p,当前驱下标等-1 时,表示找到源点。
上图的邻接矩阵
D | V0 | V1 | V2 |
---|---|---|---|
V0 | 0 | 2 | 1 |
V1 | 2 | 0 | 5 |
V2 | 1 | 5 | 0 |
从V1 -> V2
的最短路径是什么?
直接从V1 -> V2 = 5
,但是从V1 -> V0 -> V2 = 3
那么我们可以定义一个 邻接矩阵 D,代表顶点到顶点的最短路径的矩阵
初始化 邻接矩阵 D 和 图的邻接矩阵一致,
然后判断 D[1][2] > D[1][0]+D[0][2] 时,说明 顶点V1 到 顶点V2,之间 有更短的路径,此时更新 D[1][2] 位置上的权重(即V1
到顶点V2
最短路径的总权重)。
同时,定义一个 二维数组P,代表对应顶点最短路路径的前驱矩阵,
当更新某位置的权重时,同时更新 前驱矩阵 的对应位置为其前驱节点下标(比如上面的P[1][2] = P[1][0])。
佛洛依德(Floyd)算法公式:
D[V][W] = min{ D[V][W], D[V][K]+D[K][W] }
P[V][W] = P[V][K]
其中,k为从 V 到 W 的中转点。
佛洛依德(Floyd)算法代码实现:
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
#define INFINITYC 65535
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef struct
{
int vexs[MAXVEX];
int arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
}MGraph;
typedef int Patharc[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];
/* 11.1 构成邻近矩阵 */
void CreateMGraph(MGraph *G)
{
int i, j;
/* printf("请输入边数和顶点数:"); */
G->numEdges=16;
G->numVertexes=9;
for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
{
G->vexs[i]=i;
}
for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
{
for ( j = 0; j < G->numVertexes; j++)
{
if (i==j)
G->arc[i][j]=0;
else
G->arc[i][j] = G->arc[j][i] = INFINITYC;
}
}
G->arc[0][1]=1;
G->arc[0][2]=5;
G->arc[1][2]=3;
G->arc[1][3]=7;
G->arc[1][4]=5;
G->arc[2][4]=1;
G->arc[2][5]=7;
G->arc[3][4]=2;
G->arc[3][6]=3;
G->arc[4][5]=3;
G->arc[4][6]=6;
G->arc[4][7]=9;
G->arc[5][7]=5;
G->arc[6][7]=2;
G->arc[6][8]=7;
G->arc[7][8]=4;
for(i = 0; i < G->numVertexes; i++)
{
for(j = i; j < G->numVertexes; j++)
{
G->arc[j][i] =G->arc[i][j];
}
}
}
/// Floyd算法
/// @param G 图
/// @param P 前驱矩阵
/// @param D 最短路径矩阵
void ShortestPath_Floyd(MGraph G, Patharc *P, ShortPathTable *D)
{
int w, v, k;
// ✅初始化P和D
for (v = 0; v < G.numVertexes; ++v) {
for (w = 0; w < G.numVertexes; ++w) {
// ✅D[v][w]值即为对应点间的权值
(*D)[v][w] = G.arc[v][w];
// ✅初始化P P[v][w] = w
(*P)[v][w] = w;
}
}
// ✅K表示经过的中转顶点
for (k = 0; k < G.numVertexes; ++k) {
for (v = 0; v < G.numVertexes; ++v) {
for (w = 0; w < G.numVertexes; ++w) {
if ((*D)[v][w] > (*D)[v][k] + (*D)[k][w]) {
// ✅将当前两点间权值设为更小的一个
(*D)[v][w] = (*D)[v][k] + (*D)[k][w];
// ✅路径设置为经过下标为k的顶点
(*P)[v][w] = (*P)[v][k];
}
}
}
}
}
// 调用
void show(){
int v,w,k;
MGraph G;
Patharc P;
ShortPathTable D; /* 求某点到其余各点的最短路径 */
CreateMGraph(&G);
ShortestPath_Floyd(G,&P,&D);
//打印所有可能的顶点之间的最短路径以及路线值
printf("各顶点间最短路径如下:\n");
for(v=0; v %d",k);
//获得下一个路径顶点下标
k=P[k][w];
}
//打印终点
printf(" -> %d\n",w);
}
printf("\n");
}
//✅打印最终变换后的最短路径D数组
printf("最短路径D数组\n");
for(v=0; v
执行过程分析:
第一次执行:k = 0
当K = 0,所有的顶点都经过V0中转,k = 0,没有任何变化。
第二次执行:k = 1(所有顶点都经过 V1 中转)
v = 0,w = [0 - 8]
D[0][0] = 0,D[0][1] + D[1][0] = 0,不更新
D[0][1] = 1,D[0][1] + D[1][1] = 1,不更新
D[0][2] = 5,D[0][1] + D[1][2] = 1 + 3,4 < 5,更新D[0][2] = 4
D[0][3] = ∞,D[0][1] + D[1][3] = 1 + 7,8 < ∞,更新D[0][3] = 8
D[0][4] = ∞,D[0][1] + D[1][4] = 1 + 5, 6 < ∞, 更新D[0][4] = 6
D[0][5] = ∞,D[0][1] + D[1][5] = 1 + ∞, 不更新
D[0][6] = ∞,D[0][1] + D[1][6] = 1 + ∞, 不更新
D[0][7] = ∞,D[0][1] + D[1][7] = 1 + ∞, 不更新
D[0][8] = ∞,D[0][1] + D[1][8] = 1 + ∞, 不更新
v = 1,w = [0 - 8]
D[1][0] = 1,D[1][1] + D[1][0] = 1,不更新
D[1][1] = 0,D[1][1] + D[1][1] = 0,不更新
D[1][2] = 3,D[1][1] + D[1][2] = 0 + 3,不更新
D[1][3] = 7,D[1][1] + D[1][3] = 0 + 7,不更新
D[1][4] = 5,D[1][1] + D[1][4] = 0 + 5, 不更新
D[1][5] = ∞,D[1][1] + D[1][5] = 0 + ∞, 不更新
D[1][6] = ∞,D[1][1] + D[1][6] = 0 + ∞, 不更新
D[1][7] = ∞,D[1][1] + D[1][7] = 0 + ∞, 不更新
D[1][8] = ∞,D[1][1] + D[1][8] = 0 + ∞, 不更新
v = 3,w = [0 - 8]
D[3][0] = ∞,D[3][1] + D[1][0] = 7 + 1 < ∞,更新D[3][0] = 8
D[3][1] = 7,D[3][1] + D[1][1] = 7,不更新
D[3][2] = ∞,D[3][1] + D[1][2] = 7 + 3 < ∞,更新D[3][2] = 10
D[3][3] = 0,D[3][1] + D[1][3] = 7 + 7 > 0,不更新
D[3][4] = 2,D[3][1] + D[1][4] = 7 + 5 > 2, 不更新
D[3][5] = ∞,D[3][1] + D[1][5] = 7 + ∞, 不更新
D[3][6] = 3,D[3][1] + D[1][6] = 7 + ∞, 不更新
D[3][7] = ∞,D[3][1] + D[1][7] = 7 + ∞, 不更新
D[3][8] = ∞,D[3][1] + D[1][8] = 7 + ∞, 不更新
按照上面的规律,依次循环遍历,最终得到:
然后根据数组D和前驱矩阵P打印顶点和顶点之间的最短路径