數據結構與算法--圖的應用之最短路徑

數據結構與算法--圖的應用之最短路徑

前言

  上一篇簡單的介紹了圖的應用之最小生成樹的兩種求解方法,本篇來了解一下關於 圖的最短路徑 的兩種求解方法

首先看下面的圖,

從上圖中可以很直觀的看出:

最短路徑:V0 -> V1 -> V2 -> V4 -> V3 -> V6 -> V7 -> V8
最短路徑權值和: 1 + 3 + 1 + 2 + 3 + 2 + 4 = 16

那麼最短路徑怎麼求解呢?接下來Dijkstra算法

1. Dijkstra 算法

首先,我們可以將上面的圖,存儲到 鄰接矩陣 中,如下:

因爲是無向圖,所以是對稱的

我們知道,從 V0V1 的權重是 1V0V2的權重是5V1V2的權重是3

那麼,可以先從V0V1,再從V1V2,總權重是4,優於直接從 V0V2

因此,我們可以修改V0V2 的權重爲4,路徑爲 V0 -> V1 -> V2,但是我們並不能直接修改 鄰接矩陣

所以,可以定義一個 數組D,來表示V0 到某個頂點 Vw 的路徑。

定義一個數組final,來標識V0Vw直接是否有最短路徑,數組final[w] = 1,表示V0Vw有最短路徑

定義一個 數組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打印頂點和頂點之間的最短路徑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章