1. 程式人生 > >弗洛伊德(Floyd)演算法求圖的最短路徑

弗洛伊德(Floyd)演算法求圖的最短路徑

https://blog.csdn.net/jeffleo/article/details/53349825
弗洛伊德基本思想

弗洛伊德演算法作為求最短路徑的經典演算法,其演算法實現相比迪傑斯特拉等演算法是非常優雅的,可讀性和理解都非常好。

基本思想:
弗洛伊德演算法定義了兩個二維矩陣:

    矩陣D記錄頂點間的最小路徑
    例如D[0][3]= 10,說明頂點0 到 3 的最短路徑為10;
    矩陣P記錄頂點間最小路徑中的中轉點
    例如P[0][3]= 1 說明,0 到 3的最短路徑軌跡為:0 -> 1 -> 3。

它通過3重迴圈,k為中轉點,v為起點,w為終點,迴圈比較D[v][w] 和 D[v][k] + D[k][w] 最小值,如果D[v][k] + D[k][w] 為更小值,則把D[v][k] + D[k][w] 覆蓋儲存在D[v][w]中。

概念是比較難理解的,我們來看圖:

這裡寫圖片描述

頂點名稱和下標的對應
A B C D E F G
0 1 2 3 4 5 6

第2步:
以A為中間點,原D矩陣中,D[B][G]的值為INF,即不存在B->G的最小路徑,但是通過A為中間點,D[B][A] + D[A][G] = 12 + 14 = 26 小於 D[B][G] = INF, 所以D[B][A] + D[A][G] 為 B -> G的最小值,因此覆蓋D[B][G] 為 26。

第3步:
以B為中間點,第2步後的D矩陣中,D[A][C]的值為INF, 但是通過B,D[A][B] + D[B][C] = 12 + 10 = 22 小於 D[A][C] = INF,所以D[A][B] + D[B][C] 為 A->C的最小路徑,覆蓋D[A][C]的值為22, 以此類推。

第4步….
程式碼實現

我們就對上面的圖進行弗洛伊德演算法求最短路徑,並且我們求A到D的最小路徑,即v = 0, w = 3;
結構定義

typedef struct struct_graph{
char vexs[MAXN];
int vexnum;//頂點數
int edgnum;//邊數
int matirx[MAXN][MAXN];//鄰接矩陣
} Graph;

1
2
3
4
5
6

弗洛伊德演算法

//這裡是弗洛伊德演算法的核心部分
//k為中間點
for(k = 0; k < G.vexnum; k++){
//v為起點
for(v = 0 ; v < G.vexnum; v++){
//w為終點
for(w =0; w < G.vexnum; w++){
if(D[v][w] > (D[v][k] + D[k][w])){
D[v][w] = D[v][k] + D[k][w];//更新最小路徑
P[v][w] = P[v][k];//更新最小路徑中間頂點
}
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

求A 到 D的最短路徑

v = 0;
w = 3;
//求 0 到 3的最小路徑
printf("\n%d -> %d 的最小路徑為:%d\n", 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);

1
2
3
4
5
6
7
8
9
10
11

完整程式碼

include

include

define MAXN 10

define INF = 1000

typedef struct struct_graph{
char vexs[MAXN];
int vexnum;//頂點數
int edgnum;//邊數
int matirx[MAXN][MAXN];//鄰接矩陣
} Graph;

int pathmatirx[MAXN][MAXN];//記錄對應點的最小路徑的前驅點,例如p(1,3) = 2 說明頂點1到頂點3的最小路徑要經過2
int shortPath[MAXN][MAXN];//記錄頂點間的最小路徑值

void short_path_floyd(Graph G, int P[MAXN][MAXN], int D[MAXN][MAXN]){
int v, w, k;
//初始化floyd演算法的兩個矩陣
for(v = 0; v < G.vexnum; v++){
for(w = 0; w < G.vexnum; w++){
D[v][w] = G.matirx[v][w];
P[v][w] = w;
}
}

//這裡是弗洛伊德演算法的核心部分 
//k為中間點 
for(k = 0; k < G.vexnum; k++){
    //v為起點 
    for(v = 0 ; v < G.vexnum; v++){
        //w為終點 
        for(w =0; w < G.vexnum; w++){
            if(D[v][w] > (D[v][k] + D[k][w])){
                D[v][w] = D[v][k] + D[k][w];//更新最小路徑 
                P[v][w] = P[v][k];//更新最小路徑中間頂點 
            }
        }
    }
}

printf("\n初始化的D矩陣\n");
for(v = 0; v < G.vexnum; v++){
    for(w = 0; w < G.vexnum; w++){
        printf("%d ", D[v][w]);
    }
    printf("\n");
}

printf("\n初始化的P矩陣\n");
for(v = 0; v < G.vexnum; v++){
    for(w = 0; w < G.vexnum; w++){
        printf("%d", P[v][w]);
    }
    printf("\n");
}

v = 0;
w = 3;
//求 0 到 3的最小路徑
printf("\n%d -> %d 的最小路徑為:%d\n", 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);

}

int main(){
int v, w;
Graph G;
printf(“請輸入頂點數:\n”);
scanf(“%d”, &G.vexnum);
printf(“請輸入初始矩陣值:\n”);
for(v = 0; v < G.vexnum; v++){
for(w = 0; w < G.vexnum; w++){
scanf(“%d”, &G.matirx[v][w]);
}
}
printf(“\n輸入的矩陣值:\n”);
for(v = 0; v < G.vexnum; v++){
for(w = 0; w < G.vexnum; w++){
printf(“%d “, G.matirx[v][w]);
}
printf(“\n”);
}
short_path_floyd(G, pathmatirx, shortPath);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

操作結果
初始化操作

這裡寫圖片描述
弗洛伊德演算法後的D矩陣和P矩陣

這裡寫圖片描述
求得的最短路徑

這裡寫圖片描述