【數(shù)據(jù)結(jié)構(gòu)】最短路徑之迪杰斯特拉(Dijkstra)算法與弗洛伊德(Floyd)算法

圖的最短路徑

  • 對于非網(wǎng)圖】沒有邊上的權(quán)值,它的最短路徑就是兩個頂點之間經(jīng)過的邊數(shù)目最少的路徑
  • 對于網(wǎng)圖】最短路徑是指兩頂點之間經(jīng)過的邊上權(quán)值之和和最少的路徑,并且稱路徑上的第一個頂點是源點,最后一個頂點是終點忍疾。
  • 非網(wǎng)圖可以看做所有邊的權(quán)值都為1的網(wǎng)圖
  • 求解最短路徑的方法有兩種:
    • 從某個源點到其余各個頂點的最短路徑問題:迪杰斯特拉(Dijkstra)算法谨朝。
    • 圖中所有到多有頂點的最短路徑問題:弗洛伊德(Floyd)算法

本文所用圖
本文所用圖

一卤妒、迪杰斯特拉(Dijkstra)算法

迪杰斯特拉(Dijkstra)算法核心:按照路徑長度遞增的次序產(chǎn)生最短路徑。

迪杰斯特拉(Dijkstra)算法步驟:(求圖中v0到v8的最短路徑)并非一下子求出v0到v8的最短路徑字币,而是一步一步求出它們之間頂點的最短路徑则披,過過程中都是基于已經(jīng)求出的最短路徑的基礎(chǔ)上,求得更遠頂點的最短路徑洗出,最終得出源點與終點的最短路徑士复。

迪杰斯特拉(Dijkstra)算法代碼實現(xiàn)

核心代碼

/**
 * Dijkstra算法,求有向網(wǎng)G的v0頂點到其余頂點v的最短路徑P[v]及帶權(quán)長度D[v]
 * P[v]的值為前驅(qū)頂點下標翩活,D[v]表示v0到v的最短路徑長度和
 */
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc * P, ShortPathTable * D){
    
    int v, w, k = 0, min;
    int final[MAXVEX]; // final[w] = 1 表示求得頂點v0到vw的最短路徑
    
    for (v = 0; v < G.numVertexes; v++) {  // 初始化數(shù)據(jù)
        
        final[v] = 0;   // 全部頂點初始化為未知最短路徑狀態(tài)
        (*D)[v] = G.arc[v0][v];  // 將與v0點有連線的頂點加上權(quán)值
        (*P)[v] = 0;    // 初始化路徑數(shù)組P為0
    }
    
    (*D)[v0] = 0; // v0到v0路徑為0
    final[v0] = 1; // v0到v0不需要求路徑
    
    for (v = 1; v < G.numVertexes; v++) {  // 開始主循環(huán)阱洪,每次求得v0到某個v頂點的最短路徑
        
        min = INFINITY;
        
        for (w = 0; w < G.numVertexes; w++) {  // 尋找離v0最近的頂點
            if (!final[w] && (*D)[w] < min) {
                k = w;
                min = (*D)[w];   // w頂點離v0頂點更近
            }
        }
        
        final[k] = 1;  // 將目前找到的最近的頂點置為1
        
        for (w = 0; w < G.numVertexes; w++) {  // 修正當前最短路徑及距離
            if (!final[w] && (min + G.arc[k][w] < (*D)[w])) {   // 如果經(jīng)過v頂點的路徑比現(xiàn)在這條路徑的長度短的話
                                                                // 說明找到了根斷的路徑
                (*D)[w] = min + G.arc[k][w];  // 修改當前路徑長度
                (*P)[w] = k;
            }
        }
    }
}
完整代碼和測試
#include <stdio.h>
#include <stdlib.h>

#define MAXEDGE 20
#define MAXVEX 10
#define INFINITY 65535

typedef struct {
    
    int vex[MAXVEX];
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
}MGraph;

typedef int Patharc[MAXVEX];  // 用于存儲最短路徑下標的數(shù)組
typedef int ShortPathTable[MAXVEX]; // 用于存儲到各點最短路徑的權(quán)值和

/**
 * 創(chuàng)建圖
 */
void CreateMGraph(MGraph * G){
    
    int i, j;
    
    G->numVertexes = 9;  // 9個頂點
    G->numEdges = 16;  // 16條邊
    
    for (i = 0; i < G->numVertexes; i++) // 初始化圖
        G->vex[i] = i;  //給頂點編號 這里是0 到 8
    
    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] = INFINITY;
        }
    }
    
    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;
    
    // 利用鄰接矩陣的對稱性
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[j][i] = G->arc[i][j];
}

/**
 * Dijkstra算法,求有向網(wǎng)G的v0頂點到其余頂點v的最短路徑P[v]及帶權(quán)長度D[v]
 * P[v]的值為前驅(qū)頂點下標菠镇,D[v]表示v0到v的最短路徑長度和
 */
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc * P, ShortPathTable * D){
    
    int v, w, k = 0, min;
    int final[MAXVEX]; // final[w] = 1 表示求得頂點v0到vw的最短路徑
    
    for (v = 0; v < G.numVertexes; v++) {  // 初始化數(shù)據(jù)
        
        final[v] = 0;   // 全部頂點初始化為未知最短路徑狀態(tài)
        (*D)[v] = G.arc[v0][v];  // 將與v0點有連線的頂點加上權(quán)值
        (*P)[v] = 0;    // 初始化路徑數(shù)組P為0
    }
    
    (*D)[v0] = 0; // v0到v0路徑為0
    final[v0] = 1; // v0到v0不需要求路徑
    
    for (v = 1; v < G.numVertexes; v++) {  // 開始主循環(huán)冗荸,每次求得v0到某個v頂點的最短路徑
        
        min = INFINITY;
        
        for (w = 0; w < G.numVertexes; w++) {  // 尋找離v0最近的頂點
            if (!final[w] && (*D)[w] < min) {
                k = w;
                min = (*D)[w];   // w頂點離v0頂點更近
            }
        }
        
        final[k] = 1;  // 將目前找到的最近的頂點置為1
        
        for (w = 0; w < G.numVertexes; w++) {  // 修正當前最短路徑及距離
            if (!final[w] && (min + G.arc[k][w] < (*D)[w])) {   // 如果經(jīng)過v頂點的路徑比現(xiàn)在這條路徑的長度短的話
                                                                // 說明找到了根斷的路徑
                (*D)[w] = min + G.arc[k][w];  // 修改當前路徑長度
                (*P)[w] = k;
            }
        }
    }
}

int main(int argc, const char * argv[]) {
    
    int i, j, v0;
    
    MGraph G;
    Patharc P;
    
    ShortPathTable D;
    v0 = 0;  // 求v0到其余各點的最短路徑
    
    CreateMGraph(&G);
    
    ShortestPath_Dijkstra(G, v0, &P, &D);
    
    printf("源點到各個頂點的最短路徑如下:\n");
    
    for (i = 1; i < G.numVertexes; i++) {
        
        printf("v%d - v%d 最短路徑為:%2d 所經(jīng)過頂點: ",v0, i, D[i]);
        
        j = i;
        
        while (P[j] != 0) {
            printf("v%d ", P[j]);
            j = P[j];
        }
        printf("\n");
    }
    return 0;
}
測試結(jié)果
測試結(jié)果
代碼解釋
  • 用鄰接矩陣構(gòu)建圖。


  • Patharc數(shù)組存儲最短路徑下標,用ShortPathTable數(shù)組存儲各點最短路徑的權(quán)值和辟犀。
  • 最終返回的數(shù)組D和數(shù)組P俏竞,是可以得到v0到任意一個頂點的最短路徑和路徑長度的。
  • 該算法的時間復雜度為O(n^2)(因為有一個for循環(huán)嵌套)堂竟。

二、弗洛伊德(Floyd)算法

弗洛伊德(Floyd)算法是一個經(jīng)典的動態(tài)規(guī)劃算法玻佩。

弗洛伊德(Floyd)算法思路

  • 從任意節(jié)點i到任意節(jié)點j的最短路徑不外乎2種可能:1是直接從ij出嘹,2是從i經(jīng)過若干個節(jié)點kj
  • 所以咬崔,我們假設(shè)Dis(i,j)為節(jié)點u到節(jié)點v的最短路徑的距離税稼,對于每一個節(jié)點k我們檢查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立;
  • 如果成立垮斯,證明從ik再到j的路徑比i直接到j的路徑短郎仆,我們便設(shè)置Dis(i,j) = Dis(i,k) + Dis(k,j),這樣一來兜蠕,當我們遍歷完所有節(jié)點k扰肌,Dis(i,j)中記錄的便是i到j(luò)的最短路徑的距離

弗洛伊德(Floyd)算法描述

  • 任意一條單邊路徑開始熊杨。所有兩點之間的距離是邊的權(quán)曙旭,如果兩點之間沒有邊相連盗舰,則權(quán)為無窮大.
  • 對于每一對頂點uv,看看是否存在一個頂點 w使得從 uw再到 v比己知的路徑更短桂躏。如果是更新它钻趋。

弗洛伊德(Floyd)算法代碼實現(xiàn)

弗洛伊德(Floyd)算法核心代碼
/**
 * Floyd算法,求網(wǎng)圖G中各個頂點v到其余各個頂點w的最短路徑P[v][w] 以及帶權(quán)長度D[v][w]
 */
void ShortestPaht_Floyd(MGraph G, Patharc *P, ShortPathTable *D){
    
    int v,w,k;
    
    for (v = 0; v < G.numVertexes; v++) {  // 初始化D與P
        for (w = 0; w < G.numVertexes; w++) {
            (*D)[v][w] = G.arc[v][w];  //(*D)[v][w] 值即為對應(yīng)點之間的權(quán)值
            (*P)[v][w] = w;
        }
    }
    
    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]) {  // 如果經(jīng)過下標為K頂點路徑比原兩點間路徑更短
                    (*D)[v][w] = (*D)[v][k] + (*D)[k][w];  // 將當前兩點間權(quán)值設(shè)置為更小的一個
                    (*P)[v][w] = (*P)[v][k];   // 路徑設(shè)置為經(jīng)過下標為k的頂點
                }
            }
        }
    }
}
完整代碼+測試代碼
#include <stdio.h>
#include <stdlib.h>

#define MAXEDGE 20
#define MAXVEX 10
#define INFINITY 65535

typedef struct {
    
    int vex[MAXVEX];
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
}MGraph;

typedef int Patharc[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];

/**
 * 創(chuàng)建圖
 */
void CreateMGraph(MGraph * G){
    
    int i, j;
    
    G->numVertexes = 9;  // 9個頂點
    G->numEdges = 16;  // 16條邊
    
    for (i = 0; i < G->numVertexes; i++) // 初始化圖
        G->vex[i] = i;  //給頂點編號 這里是0 到 8
    
    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] = INFINITY;
        }
    }
    
    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;
    
    // 利用鄰接矩陣的對稱性
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[j][i] = G->arc[i][j];
}

/**
 * Floyd算法剂习,求網(wǎng)圖G中各個頂點v到其余各個頂點w的最短路徑P[v][w] 以及帶權(quán)長度D[v][w]
 */
void ShortestPaht_Floyd(MGraph G, Patharc *P, ShortPathTable *D){
    
    int v,w,k;
    
    for (v = 0; v < G.numVertexes; v++) {  // 初始化D與P
        for (w = 0; w < G.numVertexes; w++) {
            (*D)[v][w] = G.arc[v][w];  //(*D)[v][w] 值即為對應(yīng)點之間的權(quán)值
            (*P)[v][w] = w;
        }
    }
    
    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]) {  // 如果經(jīng)過下標為K頂點路徑比原兩點間路徑更短
                    (*D)[v][w] = (*D)[v][k] + (*D)[k][w];  // 將當前兩點間權(quán)值設(shè)置為更小的一個
                    (*P)[v][w] = (*P)[v][k];   // 路徑設(shè)置為經(jīng)過下標為k的頂點
                }
            }
        }
    }
}

int main(int argc, const char * argv[]) {

    int v, w,k;
    
    MGraph G;
    Patharc P;
    ShortPathTable D;
    
    CreateMGraph(&G);
    ShortestPaht_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");
    }    
    return 0;
}
弗洛伊德(Floyd)算法
弗洛伊德(Floyd)算法
代碼解釋
  • 弗洛伊德(Floyd)算法的代碼非常簡潔蛮位,就是一個二重初始化加上一個三重循環(huán)權(quán)值修正,就完成了所有頂點到所有頂點的最短路徑計算鳞绕。
  • 弗洛伊德(Floyd)算法的時間復雜度為O(n^3)失仁。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市猾昆,隨后出現(xiàn)的幾起案子陶因,更是在濱河造成了極大的恐慌,老刑警劉巖垂蜗,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件楷扬,死亡現(xiàn)場離奇詭異,居然都是意外死亡贴见,警方通過查閱死者的電腦和手機烘苹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來片部,“玉大人镣衡,你說我怎么就攤上這事〉涤疲” “怎么了廊鸥?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辖所。 經(jīng)常有香客問我惰说,道長,這世上最難降的妖魔是什么缘回? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任吆视,我火速辦了婚禮,結(jié)果婚禮上酥宴,老公的妹妹穿的比我還像新娘啦吧。我一直安慰自己,他們只是感情好拙寡,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布授滓。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褒墨。 梳的紋絲不亂的頭發(fā)上炫刷,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音郁妈,去河邊找鬼浑玛。 笑死,一個胖子當著我的面吹牛噩咪,可吹牛的內(nèi)容都是我干的顾彰。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼胃碾,長吁一口氣:“原來是場噩夢啊……” “哼涨享!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仆百,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤厕隧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后俄周,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吁讨,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年峦朗,在試婚紗的時候發(fā)現(xiàn)自己被綠了建丧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡波势,死狀恐怖翎朱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尺铣,我是刑警寧澤拴曲,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站凛忿,受9級特大地震影響疗韵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侄非,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望流译。 院中可真熱鬧逞怨,春花似錦、人聲如沸福澡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至除秀,卻和暖如春糯累,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背册踩。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工泳姐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暂吉。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓胖秒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親慕的。 傳聞我的和親對象是個殘疾皇子阎肝,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容