數據結構與算法--圖的應用之最短路徑
前言
上一篇簡單的介紹了圖的應用之最小生成樹的兩種求解方法,本篇來了解一下關於 圖的最短路徑 的兩種求解方法
首先看下面的圖,
從上圖中可以很直觀的看出:
最短路徑:V0 -> V1 -> V2 -> V4 -> V3 -> V6 -> V7 -> V8
最短路徑權值和: 1 + 3 + 1 + 2 + 3 + 2 + 4 = 16
那麼最短路徑怎麼求解呢?接下來Dijkstra算法
1. 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<G.numVertexes;++i)
{
printf("v%d -> 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<G.numVertexes;++i)
printf("v%d -> 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 時,表示找到源點。
2. 佛洛依德(Floyd)算法
如下圖:
上圖的鄰接矩陣
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<G.numVertexes; ++v)
{
for(w=v+1; w<G.numVertexes; w++)
{
printf("v%d-v%d weight: %d ",v,w,D[v][w]);
//獲得第一個路徑頂點下標
k=P[v][w];
//打印源點
printf(" path: %d",v);
//如果路徑頂點下標不是終點
while(k!=w)
{
//打印路徑頂點
printf(" -> %d",k);
//獲得下一個路徑頂點下標
k=P[k][w];
}
//打印終點
printf(" -> %d\n",w);
}
printf("\n");
}
//✅打印最終變換後的最短路徑D數組
printf("最短路徑D數組\n");
for(v=0; v<G.numVertexes; ++v)
{
for(w=0; w<G.numVertexes; ++w)
{
printf("%d\t",D[v][w]);
}
printf("\n");
}
//✅打印最終變換後的最短路徑P數組
printf("最短路徑P數組\n");
for(v=0; v<G.numVertexes; ++v)
{
for(w=0; w<G.numVertexes; ++w)
{
printf("%d ",P[v][w]);
}
printf("\n");
}
}
執行過程分析:
第一次執行: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打印頂點和頂點之間的最短路徑