圖的最短路徑算法

0. 前言

本文將介紹求解圖最短路徑的三個經(jīng)典算法:迪杰斯特拉 Dijkstra、弗洛伊德 Floyd卵佛、貝爾曼-福特 Bellman-Ford杨赤。

1. 迪杰斯特拉 Dijkstra

迪杰斯特拉算法,用于解決 “給定起始點到其余點的最短路徑” 問題级遭,即單源最短路徑算法望拖。時間復(fù)雜度為 O(n^2)。其本質(zhì)是貪心挫鸽。

算法步驟為:

  1. G[n][n] 二維數(shù)組記錄圖數(shù)據(jù)说敏;定義 dis[n] 一維數(shù)組記錄起始點到各點的最短路徑,初始化為 INF(可以是 int 的最大值)丢郊;visited[n] 一維數(shù)組記錄該點是否給訪問過(“訪問過”表示已找到最短路徑)盔沫,初始化為 false
  2. 選擇起始點 s 枫匾,令 dis[s] == 0架诞。
  3. 進行 n 次循環(huán):
    1. 先從 dis[n] 數(shù)組的所有未訪問結(jié)點中,找出最小值干茉,并記錄對應(yīng)下標 p 谴忧,令 visited[p] = true
    2. 更新 p 所有鄰接點在 dis[n] 數(shù)組中的值角虫,更新規(guī)則為:dis[i] = min{dis[i], dis[p]+G[p][i]}

示例及圖解:

D1.png
D2.png
D3.png
D4.png
D5.png

核心偽代碼如下:

int dis[n];
bool visited[n];
for (int i = 0; i < n; i++) {
    dis[i] = INF;
    visited[i] = false;
}

dis[s] = 0;
for (int j = 0; j < n; j++) {
    // 找 dis 數(shù)組中的最小值
    int p = -1, min = INF;
    for (int i = 0; i < n; i++) {
        if (visited[i] == false && dis[i] < min) {
            p = i;
            min = dis[i];
        }
    }
    visited[p] = true;

    // 更新最小值所有鄰接點的值
    for (int i = 0; i < n; i++) {
        if (G[p][i] == INF || visited[i]) continue;
        if (dis[i] > dis[p]+G[p][i]) {
            dis[i] = dis[p] + G[p][i];
        }
    }
}

2. 弗洛伊德 Floyd

弗洛伊德是求解圖中任意兩點間最短路徑的算法沾谓。時間復(fù)雜度為 O(n^3)。其本質(zhì)是動態(tài)規(guī)劃戳鹅。

算法步驟為:

  1. 任意兩點間的最短距離用 d(x,y) 表示均驶,初始值為兩點相連邊的權(quán)重。

  2. 遍歷所有點 k枫虏,若任意兩點 i 和 j妇穴,滿足 d(i,j) > d(i,k) + d(k,j)爬虱,則 d(i,j) = d(i,k) + d(k,j)

代碼如下:

for (k = 1; k <= n; k++) {
    for (i = 1; i <= n; i++) {
        for (j = 1; j <= n; j++) {
            if (d[i][j] > d[i][k] + d[k][j]) {
                d[i][j] = d[i][k] + d[k][j];
            }
        }
    }
}

算法分析:Floyd 的核心思想是動態(tài)規(guī)劃腾它。

  1. 我們先定義狀態(tài):d[k][i][j]跑筝,它表示經(jīng)過前 k 個節(jié)點,點 i 到點 j 的最短路徑携狭。

  2. d[k][i][j] 可以由 d[k-1][i][j] 轉(zhuǎn)移而來:

    • 假設(shè)已經(jīng)求出继蜡,經(jīng)過前 k-1 個節(jié)點,任意兩點間的最短路徑逛腿。
    • 那么,d[k][i][j] 就是 經(jīng)過前 k-1 個節(jié)點 i 到 j 最短路徑經(jīng)過第 k 個節(jié)點 i 到 j 最短路徑 中的最小值仅颇。
    • 而經(jīng)過第 k 個節(jié)點 i 到 j 最短路徑单默,就是 i 到 k 的最短路徑加上 k 到 j 的最短路徑。
    • 最終忘瓦,得出狀態(tài)轉(zhuǎn)移方程為:d[k][i][j] = min{d[k-1][i][j], d[k-1][i][k] + d[k-1][k][j]}搁廓。
  3. 由于 d[k][x][x] 的狀態(tài)僅由 d[k-1][x][x] 轉(zhuǎn)移而來,所以我們可以進行優(yōu)化:d[i][j] = min{d[i][j], d[i][k] + d[k][j]}耕皮。

3. 貝爾曼-福特 Bellman-Ford

貝爾曼-福特算法境蜕,也是一個單源最短路徑算法,同時它還能處理負權(quán)邊凌停。算法時間復(fù)雜度為 O(NE)粱年,N 是點的個數(shù),E 是邊的個數(shù)罚拟。

算法步驟:

  1. 令源點為 s 台诗,源點到任意點 x 的最短距離用 d(x) 表示。d(s) 初始值為0赐俗,其余初始值為無窮拉队。

  2. 進行 N-1 次松弛操作,松弛操作即:遍歷所有邊阻逮,對于每一條邊 e(i,j) 粱快,如果存在 d(j) > d(i) + e(i,j) ,則令 d(j) = d(i) + e(i,j)叔扼。

代碼如下:

for (i = 0; i < n-1; i++) {
    for (j = 0; j < E; j++) {
        if (d(e[j].to) > d(e[j].from) + e[j]) {
            d(e[j].to) = d(e[j].from) + e[j];
        }
    }
}

算法分析:松弛操作的過程十分神奇事哭,直覺告訴我它肯定是正確的,但具體原因我也是一頭霧水币励。不過慷蠕,我們可以知道,每次松弛操作后食呻,至少能確定一個點的最短路徑流炕。所以澎现,需要進行 N-1 次。

Bellman-Ford 如何解決 Dijkstra 不能解決的負權(quán)邊問題呢每辟?如下圖剑辫,源點為 1 。若在 Dijkstra 中渠欺,第二次大循環(huán)時便會確定源點到點 3 的最短距離為 1 妹蔽;而在 Bellman-Ford 中,經(jīng)過松弛操作便可以確定源點到點 3 的最短距離為 -1 挠将。

6.png

Bellman-Ford 算法雖然能解決負權(quán)邊的問題胳岂,但時間復(fù)雜度還是偏高,當用于稠密圖時舔稀,是無法接受的乳丰。

因此,有人提出了 Bellman-Ford 的優(yōu)化算法:SPFA内贮。即第一次松弛操作产园,只需要對源點的鄰接邊進行即可;第二次松弛操作夜郁,只需要對與這些邊相連點的鄰接邊進行即可装悲;以此類推尤辱,直至所有邊遍歷完。這類似于 BSF 。

4. 參考

【原創(chuàng)】算法系列——四種最短路算法:Floyd护昧,Dijkstra滥玷,Bellman-Ford两疚,SPFA

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末量窘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赵颅,更是在濱河造成了極大的恐慌虽另,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饺谬,死亡現(xiàn)場離奇詭異捂刺,居然都是意外死亡,警方通過查閱死者的電腦和手機募寨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門族展,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拔鹰,你說我怎么就攤上這事仪缸。” “怎么了列肢?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵恰画,是天一觀的道長宾茂。 經(jīng)常有香客問我,道長拴还,這世上最難降的妖魔是什么跨晴? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮片林,結(jié)果婚禮上端盆,老公的妹妹穿的比我還像新娘。我一直安慰自己费封,他們只是感情好焕妙,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弓摘,像睡著了一般访敌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上衣盾,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音爷抓,去河邊找鬼势决。 笑死,一個胖子當著我的面吹牛蓝撇,可吹牛的內(nèi)容都是我干的果复。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼渤昌,長吁一口氣:“原來是場噩夢啊……” “哼虽抄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起独柑,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤迈窟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后忌栅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體车酣,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年索绪,在試婚紗的時候發(fā)現(xiàn)自己被綠了湖员。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡瑞驱,死狀恐怖娘摔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唤反,我是刑警寧澤凳寺,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布鸭津,位于F島的核電站,受9級特大地震影響读第,放射性物質(zhì)發(fā)生泄漏曙博。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一怜瞒、第九天 我趴在偏房一處隱蔽的房頂上張望父泳。 院中可真熱鬧,春花似錦吴汪、人聲如沸惠窄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杆融。三九已至,卻和暖如春霜运,著一層夾襖步出監(jiān)牢的瞬間脾歇,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工淘捡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留藕各,地道東北人。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓焦除,卻偏偏與公主長得像激况,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子膘魄,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

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