最短路径之迪杰斯特拉(Dijkstra 算法)弗洛伊德算法(C语言完整代码实现)
寫在前面:博主是一位普普通通的19屆雙非軟工在讀生,平時最大的愛好就是聽聽歌,逛逛B站。博主很喜歡的一句話花開堪折直須折,莫待無花空折枝:博主的理解是頭一次為人,就應該做自己想做的事,做自己不后悔的事,做自己以后不會留有遺憾的事,做自己覺得有意義的事,不浪費這大好的青春年華。博主寫博客目的是記錄所學到的知識并方便自己復習,在記錄知識的同時獲得部分瀏覽量,得到更多人的認可,滿足小小的成就感,同時在寫博客的途中結交更多志同道合的朋友,讓自己在技術的路上并不孤單。
目錄:
1.求最短路徑的兩種算法簡介
2.迪杰斯特拉(Dijkstra 算法)
???? ?? 迪杰斯特拉算法簡介
???? ?? 迪杰斯特拉算法代碼實現(C完整代碼)
???? ?? 迪杰斯特拉算法小結
3.弗洛伊德算法
???? ?? 弗洛伊德算法簡介
???? ?? 迪杰斯特拉算法代碼實現(C完整代碼)
???? ?? 弗洛伊德算法小結
1.求最短路徑的兩種算法簡介
在一個網(有權圖)中,求一個頂點到另一個頂點的最短路徑的計算方式有兩種:迪杰斯特拉(Dijkstra 算法)和弗洛伊德(Floyd)算法。迪杰斯特拉算法計算的是有向網中的某個頂點到其余所有頂點的最短路徑;弗洛伊德算法計算的是任意兩頂點之間的最短 路徑。 最短路徑算法既適用于有向網,也同樣適用于無向網。本節將圍繞有向網講解迪杰斯特拉算法的具體實現
2.迪杰斯特拉(Dijkstra 算法)
2.1迪杰斯特拉算法簡介
迪杰斯特拉算法計算的是從網中一個頂點到其它頂點之間的最短路徑問題
如圖 所示是一個有向網,在計算 V0 到其它所有頂點之間的最小路徑時,迪杰斯特拉算法的計算方式為:
從 V0 出發,由于可以直接到達 V2 、V3和 V5 ,而其它頂點和 V0 之間沒有弧的存在,所以之間的距離設定為無窮大,可以得到 下面這個表格:
從表格中可以看到,V0 到 V2 的距離最近,所以迪杰斯特拉算法設定 V0-V2 為 V0 到 V2 之間的最短路徑,最短路徑的權 值和為 10
已經判斷 V0-V2 是最短路徑,所以以 V2 為起始點,判斷 V2 到除了 V0 以外的其余各點之間的距離,如果對應的權值比前 一張表格中記錄的數值小,就說明網中有一條更短的路徑,直接更新表格;反之表格中的數據不變。可以得到下面這個表格:
例如,表格中 V0 到 V3 的距離,發現當通過 V2 到達 V3 的距離比之前的 ∞ 要小,所以更新表格。
更新之后,發現 V0-V4 的距離最近,設定 V0 到 V4 的最短路徑的值為 30。之后從 V4 出發,判斷到未確定最短路徑的其 它頂點的距離,繼續更新表格
更新后確定從 V0 到 V3 的最短路徑為 V0-V4-V3,權值為 50。然后從 V3 出發,繼續判斷:
對于 V5 來說,通過 V0-V4-V3-V5 的路徑要比之前的權值 90 還要小,所以更新表格,更新后可以看到,V0-V5 的距離此時 最短,可以確定 V0 到 V5 的最短路徑為 60。
最后確定 V0-V1 的最短路徑,由于從 V0 無法到達 V1 ,最終設定 V0 到 V1 的最短路徑為 ∞(無窮大)。在確定了 V0 與其他所有頂點的最短路徑后,迪杰斯特拉算法才算結束。
實際上無向網中的最短路徑問題也可以使用迪杰斯特拉算法解決, 解決過程和上述過程完全一致。
2.2迪杰斯特拉算法代碼實現(C完整代碼)
#include <stdio.h> #define MAX_VERtEX_NUM 20 //頂點的最大個數 #define VRType int //表示弧的權值的類型 #define VertexType int //圖中頂點的數據類型 #define INFINITY 65535 typedef struct {VertexType vexs[MAX_VERtEX_NUM]; //存儲圖中頂點數據VRType arcs[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; //二維數組,記錄頂點之間的關系int vexnum,arcnum; //記錄圖的頂點數和弧(邊)數 }MGraph; typedef int PathMatrix[MAX_VERtEX_NUM]; //用于存儲最短路徑中經過的頂點的下標 typedef int ShortPathTable[MAX_VERtEX_NUM]; //用于存儲各個最短路徑的權值和 //根據頂點本身數據,判斷出頂點在二維數組中的位置 int LocateVex(MGraph * G,VertexType v){int i=0;//遍歷一維數組,找到變量 vfor (; i<G->vexnum; i++) {if (G->vexs[i]==v) {break;}} //如果找不到,輸出提示語句,返回-1 if (i>G->vexnum) {printf("no such vertex.\n"); return -1;}return i; } //構造有向網 void CreateUDG(MGraph *G){ scanf("%d,%d",&(G->vexnum),&(G->arcnum));for (int i=0; i<G->vexnum; i++) {scanf("%d",&(G->vexs[i]));}for (int i=0; i<G->vexnum; i++) {for (int j=0; j<G->vexnum; j++) {G->arcs[i][j]=INFINITY;}} for (int i=0; i<G->arcnum; i++) {int v1,v2,w; scanf("%d,%d,%d",&v1,&v2,&w);int n=LocateVex(G, v1);int m=LocateVex(G, v2);if (m==-1 ||n==-1) {printf("no this vertex\n"); return;}G->arcs[n][m]=w;} } //迪杰斯特拉算法,v0 表示有向網中起始點所在數組中的下標 void ShortestPath_Dijkstra(MGraph G,int v0,PathMatrix *p,ShortPathTable *D){int final[MAX_VERtEX_NUM];//用于存儲各頂點是否已經確定最短路徑的數組 //對各數組進行初始化for (int v=0; v<G.vexnum; v++) {final[v]=0;(*D)[v]=G.arcs[v0][v];(*p)[v]=0; } //由于以 v0 位下標的頂點為起始點,所以不用再判斷(*D)[v0]=0;final[v0]=1;int k = 0;for (int i=0; i<G.vexnum; i++) {int min=INFINITY; //選擇到各頂點權值最小的頂點,即為本次能確定最短路徑的頂點for (int w=0; w<G.vexnum; w++) {if (!final[w]) {if ((*D)[w]<min) {k=w; min=(*D)[w];}} } //設置該頂點的標志位為 1,避免下次重復判斷final[k]=1; //對 v0 到各頂點的權值進行更新for (int w=0; w<G.vexnum; w++) {if (!final[w]&&(min+G.arcs[k][w]<(*D)[w])) {(*D)[w]=min+G.arcs[k][w];(*p)[w]=k;//記錄各個最短路徑上存在的頂點}}} } int main(){MGraph G;CreateUDG(&G);PathMatrix P;ShortPathTable D;ShortestPath_Dijkstra(G, 0, &P, &D);for (int i=1; i<G.vexnum; i++) {printf("V%d - V%d 的最短路徑中的頂點有(逆序):",0,i);printf(" V%d",i);int j=i; //由于每一段最短路徑上都記錄著經過的頂點,所以采用嵌套的方式輸出即可得到各個最短路徑上的所有頂點while (P[j]!=0) {printf(" V%d",P[j]);j=P[j];}printf(" V0\n");}printf("源點到各頂點的最短路徑長度為:\n");for (int i=1; i<G.vexnum; i++) {printf("V%d - V%d : %d \n",G.vexs[0],G.vexs[i],D[i]);}return 0; }對于本例子:
輸入 6,8 0 1 2 3 4 5 0,5,100 0,4,30 0,2,10 1,2,5 2,3,50 3,5,10 4,3,20 4,5,60 //輸出 V0 - V1 的最短路徑中的頂點有: V1 V0 V0 - V2 的最短路徑中的頂點有: V2 V0 V0 - V3 的最短路徑中的頂點有: V3 V4 V0 V0 - V4 的最短路徑中的頂點有: V4 V0 V0 - V5 的最短路徑中的頂點有: V5 V3 V4 V0 源點到各頂點的最短路徑長度為: V0 - V1 : 65535 V0 - V2 : 10 V0 - V3 : 50 V0 - V4 : 30 V0 - V5 :2.3迪杰斯特拉算法小結
迪杰斯特拉算法解決的是從網中的一個頂點到所有其它頂點之間的最短路徑,算法整體的時間復雜度為 O(n2)。但是如果需要求 任意兩頂點之間的最短路徑,使用迪杰斯特拉算法雖然最終雖然也能解決問題,但是大材小用,相比之下使用弗洛伊德算法解決 此類問題會更合適。
3.弗洛伊德算法
3.1弗洛伊德算法簡介
弗洛伊德的核心思想是:對于網中的任意兩個頂點(例如頂點 A 到頂點 B)來說,之間的最短路徑不外乎有 2 種情況:
所以,弗洛伊德算法的核心為:對于從頂點 A 到頂點 B 的最短路徑,拿出網中所有的頂點進行如下判斷:
Dis(A,K)+ Dis(K,B)< Dis(A,B)
其中,K 表示網中所有的頂點;Dis(A,B) 表示頂點 A 到頂點 B 的距離。
也就是說,拿出所有的頂點 K,判斷經過頂點 K 是否存在一條可行路徑比直達的路徑的權值小,如果式子成立,說明確實存在一條權值更小的路徑,此時只需要更新記錄的權值和即可。
任意的兩個頂點全部做以上的判斷,最終遍歷完成后記錄的最終的權值即為對應頂點之間的最短路徑
如下圖:
例如,在使用弗洛伊德算法計算上圖中的任意兩個頂點之間的最短路徑時,具體實施步驟為: 首先,記錄頂點之間初始的權值,如下表所示:
依次遍歷所有的頂點,假設從 V0 開始,將 V0 作為中間點,看每對頂點之間的距離值是否會更小。最終 V0 對于每對頂點之 間的距離沒有任何改善。
對于 V0 來說,由于該頂點只有出度,沒有入度,所以沒有作為中間點的可能。同理,V1 也沒有可能。
將 V2 作為每對頂點的中間點,有影響的為 (V0,V3) 和 (V1,V3):
例如,(V0,V3)權值為無窮大,而(V0,V2)+(V2,V3)= 60,比之前的值小,相比而言后者的路徑更短;同理 (V1, V3)也是如此。
更新的表格為:
以 V3 作為中間頂點遍歷各隊頂點,更新后的表格為:
以 V4 作為中間頂點遍歷各隊頂點,更新后的表格為:
而對于頂點 V5 來說,和頂點 V0 和 V1 相類似,所不同的是,V5 只有入度,沒有出度,所以對各隊頂點的距離不會產生影 響。最終采用弗洛伊德算法求得的各個頂點之間的最短路徑如上圖所示。
3.2弗洛伊德算法代碼實現(C語言完整代碼)
#include <stdio.h> #define MAX_VERtEX_NUM 20 //頂點的最大個數 #define VRType int //表示弧的權值的類型 #define VertexType int //圖中頂點的數據類型 #define INFINITY 65535 typedef struct {VertexType vexs[MAX_VERtEX_NUM]; //存儲圖中頂點數據VRType arcs[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; //二維數組,記錄頂點之間的關系int vexnum,arcnum; //記錄圖的頂點數和弧(邊)數 }MGraph; typedef int PathMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; //用于存儲最短路徑中經過的頂點的下標 typedef int ShortPathTable[MAX_VERtEX_NUM][MAX_VERtEX_NUM]; //用于存儲各個最短路徑的權值和 //根據頂點本身數據,判斷出頂點在二維數組中的位置 int LocateVex(MGraph * G,VertexType v){int i=0; //遍歷一維數組,找到變量 vfor (; i<G->vexnum; i++) {if (G->vexs[i]==v) {break;}} //如果找不到,輸出提示語句,返回-1if (i>G->vexnum) {printf("no such vertex.\n"); return -1;}return i;} //構造有向網 void CreateUDG(MGraph *G){ scanf("%d,%d",&(G->vexnum),&(G->arcnum));for (int i=0; i<G->vexnum; i++) { scanf("%d",&(G->vexs[i]));}for (int i=0; i<G->vexnum; i++) {for (int j=0; j<G->vexnum; j++) {G->arcs[i][j]=INFINITY;}} for (int i=0; i<G->arcnum; i++) {int v1,v2,w;scanf("%d,%d,%d",&v1,&v2,&w);int n=LocateVex(G, v1);int m=LocateVex(G, v2);if (m==-1 ||n==-1) {printf("no this vertex\n"); return;}G->arcs[n][m]=w;} } //弗洛伊德算法,其中 P 二維數組存放各對頂點的最短路徑經過的頂點,D 二維數組存儲各個頂點之間的權值 void ShortestPath_Floyed(MGraph G,PathMatrix *P,ShortPathTable *D){ //對 P 數組和 D 數組進行初始化for (int v=0; v<G.vexnum; v++) {for (int w=0; w<G.vexnum; w++) {(*D)[v][w]=G.arcs[v][w];(*P)[v][w]=-1;}}//拿出每個頂點作為遍歷條件for (int k=0; k<G.vexnum; k++) { //對于第 k 個頂點來說,遍歷網中任意兩個頂點,判斷間接的距離是否更短for (int v=0; v<G.vexnum; v++) {for (int w=0; w<G.vexnum; w++) { //判斷經過頂點 k 的距離是否更短,如果判斷成立,則存儲距離更短的路徑if ((*D)[v][w] > (*D)[v][k] + (*D)[k][w]) {(*D)[v][w]=(*D)[v][k] + (*D)[k][w];(*P)[v][w]=k;}}}} } int main(){MGraph G;CreateUDG(&G);PathMatrix P;ShortPathTable D;ShortestPath_Floyed(G, &P, &D);for (int i=0; i<G.vexnum; i++) {for (int j=0; j<G.vexnum; j++) {printf("%d ",P[i][j]);}printf("\n");}for (int i=0; i<G.vexnum; i++) {for (int j=0; j<G.vexnum; j++) {printf("%d ",D[i][j]);}printf("\n");}return 0; }對于這個例子
//輸入 6,8 0 1 2 3 4 5 0,5,100 0,4,30 0,2,10 1,2,5 2,3,50 3,5,10 4,3,20 4,5,60 //輸出 -1 -1 -1 4 -1 4 -1 -1 -1 2 -1 3 -1 -1 -1 -1 -1 3 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 3 -1 -1 -1 -1 -1 -1 65535 65535 10 50 30 60 65535 65535 5 55 65535 65 65535 65535 65535 50 65535 60 65535 65535 65535 65535 65535 10 65535 65535 65535 20 65535 30 65535 65535 65535 65535 65535 655353.3弗洛伊德算法小結
迪杰斯特拉主要解決從網(帶權圖)中某一頂點計算到其它頂點之間的最短路徑問題。如果求有向網 中每一對頂點之間的最短路徑,使用迪杰斯特拉算法的解決思路是:以每一個頂點為源點,執行迪杰斯特拉算法。這樣可以求得 每一對頂點之間的最短路徑。也可以使用弗洛伊德算法,該算法相比于使用迪杰斯特拉算法在解決此問題上的時間復雜度雖然相同,都為 O(n3),但是弗洛伊德算法的實現形式更簡單。
本篇博客轉載C語言中文網
總結
以上是生活随笔為你收集整理的最短路径之迪杰斯特拉(Dijkstra 算法)弗洛伊德算法(C语言完整代码实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度、广度优先生成树(C完整代码)
- 下一篇: 拓扑排序(完整案列及C语言完整代码实现)