最短路徑演算法

2021-09-12 17:52:12 字數 3659 閱讀 2118

參考部落格:最短路徑—dijkstra演算法和floyd演算法

最短路徑問 題。即尋找圖中某兩個特定結點間最短的路徑長度。所謂圖上的路徑,即從圖中 乙個起始結點到乙個終止結點途中經過的所有結點序列,路徑的長度即所經過的邊權和。

floyd演算法又被稱為佛洛依德演算法,其演算法思路如下:

用鄰接矩陣儲存原圖,那麼此時鄰接矩陣中edge[i][j]的值即表示從結點 i 到 結點j,中間不經過任何結點時距離的最小值(若它們之間有多條邊,取最小權值儲存至鄰接矩陣;也可能為無窮,即不可達)。

假設結點編號為 1 到 n,考慮從結點i 到結點j 中間只能經過編號小於等於1的結點(也可以不經過)時最短路徑長度。與原始狀況相比,在中間路徑上可以經過的結點增加了編號為1 的結點。又知最短路徑上的結點一定不會出現重複(不考慮存在負權值的情況)。那麼,某兩個結點間若由於允許經過結點 1 而出現了新的最短路徑, 則該路徑被結點 1 分割成兩部分:由 i 到結點 1,同時中間路徑上不經過結點 1 的第一段路徑;由結點 1 到 j,中間路徑上同樣不經過結點 1 的第二段路徑,其 路徑總長度為edge[i][1] + edge[1][j]

要確定該路徑是否比不允許經過結點1時更短,比較edge[i][1] + edge[1][j]edge[i][j]之間的大小關係。若前者較小, 則說明中間路徑經過結點1時比原來更短,則用該值代表由i 到j 中間路徑結點編號小於等於1的最短路徑長度;否則,該路徑長度將依然保持原值edge[i][j], 即雖然允許經過結點1,但是不經過時路徑長度最短。

考慮更一般的情況,若edge[i][j]表示從結點i到結點j,中間只能經過編號小於k的點時的最短路徑長度,可以由這些值確定當中間允許經過編號小於等於k的結點時,它們之間的最短路徑長度。同樣,與原情況相比,新情況中允許出現在中間路徑的結點新增了編號為 k 的結點,同理確定edge[i][k] + edge[k][j]的值與edge[i][j]的值,若前者較小則該值代表了新情況中從結點i到結點j的最短路徑長度;否則,新情況中該路徑長度依舊保持不變。

如上,在圖的鄰接矩陣表示法中,edge[i][j]表示由結點i到結點j中間不經過任何結點時的最短距離,那麼依次為中間允許經過的結點新增結點 1、結點 2、……直到結點n,當新增完這些結點後,從結點i到結點j允許經過所有結點的最短路徑長度就可以確定了,該長度即為原圖上由結點 i 到結點 j 的 最短路徑長度。

ans[k][i][j]為從結點i到結點j允許經過編號小於等於k的結點時其最短路徑長度。如上,ans[0][i][j]即等於圖的鄰接矩陣表示中edge[i][j]的值。通過如下迴圈,完成所有k對應的ans[k][i][j]值的求解:

for (int k = 1;k <= n;k ++)  

if (ans[k - 1][i][j] == 無窮 || ans[k - 1][i][k] + ans[k - 1][k][j] < ans[k - 1][i][j]) else

} }}

經過這樣的n 次迴圈後,即可得到所有結點間允許經過所有結點條件下的最短路徑長度,該路徑長度即為要求的最短路徑長度。即若要求得 ab 之間的最短路徑長度,其答案為ans[n][a][b]的值。

同時注意到,在通過ans[k - 1][i][j]的各值來遞推求得ans[k][i][j]的 值時,所有的ans[k][i][j]值將由ans[k - 1][i][j]ans[k - 1][i][k] + ans[k - 1][k][j]的大小關係確定,但同時ans[k][i][k]ans[k][k][j]必定與ans[k - 1][i][k]和ans[k - 1][k][j]的值相同,即這些值不會因為本次更新而發生改變。所以將如上**片段簡化成如下形式:

for (int k = 1;k <= n;k ++)  

} }

}

如該**片段所示,將原本的三維陣列簡化為二維陣列,而每次更新時直接在該二維陣列上進行更新。這是有原因的,當最外層迴圈由k - 1變為k時, 各ans[i][k]ans[k][j]的值不會因為本次更新發生改變(當前 i 到 k 的最短路徑中途必不經過結點k),而本次更新又是由它們的值和各ans[i][j]的值比較而進行的。所以直接在二維陣列上進行本次更新,並不會影響到本次更新中其它各值的判定。節省了大量的記憶體空間,同時還省略了保持原值的操作。

與之前討論的 floyd 演算法有乙個非常明顯的區別,floyd 演算法可以計算出圖上所有結點對之間的最短路徑長度,dijkstra演算法只能求得某特定結點到其它所有結點的最短路徑長度,即單源最短路路徑問題。

那麼該如何確定某特定結點到其它所有結點的最短距離呢?我們將按照最短路徑長度遞增的順序確定每乙個結點的最短路徑長度,即先確定的結點的最短路徑長度不大於後確定的結點的最短路徑長度。這樣有乙個好處,當確定乙個結點的最短路徑長度時,該最短路徑上所有中間結點的最短路徑長度必然已經被確定了(中間路徑的最短路徑長度必小於這個結點的最短路徑長度)。

不妨設有結點1 到 n,我們將要求從結點 1 出發到其它所有結點的最短路徑長度。初始時,設結點 1 到其它所有點的最短路徑長度均為無窮(或不確定的),我們立即確定由結點 1 到結點 1 的最短路徑長度距離為 0。設已經確定最短路徑長度的結點集合為集合 k,於是我們將結點 1 加入該集合。

將問題一般化,假設集合 k 中已儲存最短路徑長度最短的前 m 個結點,它們是 p1,p2……pm,並已經得出它們的最短路徑長度。那麼第 m+1 近的結點與結點 1 的最短路徑上的中間結點一定全部屬於集合 k,這是因為若最短路徑上中間有乙個不屬於集合 k 的結點,則它的最短路徑距離一定小於第 m+1 近的結點的最短路徑長度,與距離小於第

m+1 近的結點的最短路徑已經全部確定、這樣的結點全部屬於集合 k 矛盾。那麼第 m+1 近結點的最短路徑必是由以下兩部分組成,從結點 1 出發經由已經確定的最短路徑到達集合 k 中的某結點 2,再由 結點2 經過一條邊到達該結點。為此,我們遍歷與集合 k 中結點直接相鄰的邊,設其為(u,v,c),其中 u 屬於集合k,v 不屬於集合 k,計算由結點 1 出發經過已經確定的最短路到達結點 u,再由結點 u 經過該邊到達結點 v 的路徑長度。該路徑長度為已經確定的結點 u 的最短路徑長度+c。所有與集合 k 直接相鄰的非集合 k 結點中,該路徑長度最短的那個結點即確定為第 m+1 近結點,並將該點加入集合 k。如此往復,直到結點 1 到所有結點的最短路徑全部確定。

如上文所示,dijkstra 演算法流程如下:

1.初始化,集合 k 中加入結點 1,結點 1 到結點 1 最短距離為 0,到其它結點為無窮(或不確定)。

2.遍歷與集合 k 中結點直接相鄰的邊(u,v,c),其中 u 屬於集合 k,v不屬於集合 k,計算由結點 1 出發按照已經得到的最短路到達 u,再由 u 經過該邊到達 v 時的路徑長度。比較所有與集合 k 中結點直接相鄰的非集合 k 結點間的路徑長度,其中路徑長度最小的結點被確定為下乙個最短路徑確定的結點,其最短路徑長度即為這個路徑長度,最後將該結點加入集合 k。

3.若集合 k 中已經包含了所有的點,演算法結束;否則重複步驟 2。

最短路徑演算法 最短路

在每年的校賽裡,所有進入決賽的同學都會獲得一件很漂亮的t shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?input 輸入包括多組資料。每組資料第一行是兩個整數n m n 100,m 10000 n...

最短路徑演算法

floyd演算法 012345 001 54 1108 1 2 801 3 3 1035 45 302 5413520 floyd 演算法過程描述如下 首先 以邊集 初始化,得到所有的直接連通代價 依次考慮第 k個結點,對於 中的每乙個 i j 判斷是否滿足 s i j s i k s k j 如果...

最短路徑演算法

個人覺得下面 有代表性 最短路徑演算法原始碼 vb 本人載 開發gis,遊自編的最短路徑查詢程式,速度特快,3萬節點,35000條路全部遍歷,只需1秒。現將最短路徑的思路告訴大家,希望大家在優化,並用不同語言編制,我正在學delphi,準備用delphi做成庫,本例以由拓撲關係的arc info 檔...