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