帶權圖的最短路徑算法(Dijkstra,Floyd,Bellman_ford)


Dijkstra算法 —— 計算非負權值的單源最短路徑

算法思想

  基於貪心策略,每次都選擇與源點 S 距離最近的且尚未確認最短路徑的宿點 D,認為當前 S-D 的距離就是最終 S-D 的最短路徑,因為 S 到其它點的距離都大於 S-D,所以 S 經過其它點再到達 D 點的路徑必然更加大於 S-D,因此,當前 S-D 則為 S 到 D的最短路徑。注意,此時的 S-D 並不一定是SD邊的長度,很有可能是途徑了其它一些已經確認了最短路徑的點的長度。確定了 S 到 D 的最短路徑之后,要更新 S 到其它尚未確定的點如點 E 的距離,判斷經過 S-D-E 是否小於 S-E,若小於,則更新 S-E 的值。全部更新完后,繼續尋找距離 S 最近的且尚未確認的宿點 D',同點 D 一樣,此時 S-D' 就是 S 到 D' 的最短距離,然后繼續更新剩余的宿點,直到全部確認最短路徑。

  該算法的要點在於每次確定了一個最短路徑的點,都要更新一遍源點到其它未確認的點的距離,以此來保證下次找到的路徑最短的點的路徑就是最短路徑,這也是該貪心策略成功的關鍵。不過該算法不能處理帶負權值的圖,貪心得不到最優解。

Bellman-ford算法 —— 可計算帶負權值的單源最短路徑

算法思想

  初始時,將源點 S 到其它點的距離 D(x) 設為無窮,然后針對圖中每一條邊 <u, v> ,進行如下松弛處理:D(v) = min ( D(v), D(u) + uv ) 。上述的遍歷操作 最多進行 n - 1 次,就可以得到所有點的最短路徑。之后再進行一次遍歷,若再出現 D(v) > D(u) + uv 情況,那只能說明圖中存在負權值環路,無法找到最短路徑,只會越松弛越短。

  至於為什么最多進行 n - 1 次松弛遍歷操作就可以確定 S 到達其它 n - 1 個點的最短路徑呢,因為每一次遍歷完成,至少會得到一個點的最短路徑,原因如下(懶得畫圖,腦中思考一下就好):

  假設 S 的可直達相鄰節點有 A B C 三點(也可以更多,不過原理一樣),顯然 SA , SB, SC 至少有一個是最短路徑,那么在一次松弛之后,D(A),D(B),D(C)至少有一個是最短路徑,假設D(A)為最短路徑,即為 SA,接下來分兩種情況,

  (1)S 到 B C 兩點的最短路徑全都需要經過 A 點,那么與 A 直接相鄰的節點 D E F 的最短路徑肯定要經過 A 點,此時類比 S 和 ABC 三點,SAD, SAE, SAF 也至少有一個是最短路徑,因此第二次松弛后,至少又能確定一個點,假設為 D。D 點有可能在第一次松弛時就已經確定了,這是由邊的遍歷順序決定的。

  (2)S 到 B或 C 的最短路徑不需要經過 A ,那么第一次遍歷時就已經確定了 A 和 B C 中的某一點。

  因此每次松弛至少可以確定一個點的最短路徑,且只有 S,A,D.....一直類似情況(1)時才最多需要 n - 1 次松弛。

Floyd算法 —— 計算圖中任意兩點之間的最短路徑

算法思想

  該算法基於動態規划思想,對於任意兩點 m, n,m 到 n 的最短路徑可能經過 1,2,3,4 ...中任意個點,因此首先計算可以經過第一個點時,m  n 的最短路徑是多少:dp[m][n] = min ( dp[m][1] + dp[1][n], dp[m][n] ),當計算完任意兩個點后,再判斷可以經過前兩個點時,m n 的最短路徑:dp[m][n] = min ( dp[m][2] + dp[2][n], dp[m][n] ),若不經過第二個點,那么dp[m][n]不變,若最短路徑需要經過第二個點,那么dp[m][n] = dp[m][2] + dp[2][n],至於此時經不經過第一個點,先經過還是后經過根本無需考慮,因為dp[m][2] 和 dp[2][n] 代表的就是考慮過第一個點的情況下的最短路徑,在第一次只考慮第一個點時已經計算過了。下面繼續計算可以經過前3個點、4個點......直到n個點的情況。

  動態規划的最優子結構在於:在可以經過前 k-1 個點的情況下,已知任意兩點 m n 的最短路徑,那么在可以經過前 k 個點時,m n 的最短路徑可以通過:

dp[m][n] = min ( dp[m][k] + dp[k][n], dp[m][n] )

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM