2018-03-13 17:08:57
最短路徑問題是圖論中一個經典的問題,Dijkstra算法更是大名鼎鼎。然而縱是如此著名的算法也有其不擅長的領域,也就是帶有負權邊的圖是無法使用Dijkstra算法來進行最短路計算的。理由也很簡單,每次dijkstra都是將目前的額最短路添加到集合中,這也就保證了,下一次的最短路徑是肯定要長於之前添加進去的最短路徑的,然而在有負權邊的時候,這個推論就會被打破,最后會導致整個Dijkstra算法崩潰。
那么,難道帶有負權邊的最短路徑問題就沒有解法了么?
答案顯然是否定的,這里就會介紹兩種求解帶有負權邊的圖的最短路徑求解的算法,一是Bellman Ford算法,二是SPFA算法。
一、Bellman Ford算法
貝爾曼-福特算法(英語:Bellman–Ford algorithm),求解單源最短路徑問題的一種算法,由理查德·貝爾曼(Richard Bellman) 和 萊斯特·福特 創立的。有時候這種算法也被稱為 Moore-Bellman-Ford 算法,因為Edward F. Moore 也為這個算法的發展做出了貢獻。它的原理是對圖進行V-1次松弛操作,得到所有可能的最短路徑。其優於迪科斯徹算法的方面是邊的權值可以為負數、實現簡單,缺點是時間復雜度過高,高達O(VE)。但算法可以進行若干種優化,提高了效率。
Bellman Ford算法每次對所有的邊進行松弛,每次松弛都會得到一條最短路徑,所以總共需要要做的松弛操作是V - 1次。在完成這么多次松弛后如果還是可以松弛的話,那么就意味着,其中包含負環。
procedure BellmanFord(list vertices, list edges, vertex source) // 該實現讀入邊和節點的列表,並向兩個數組(distance和predecessor)中寫入最短路徑信息 // 步驟1:初始化圖 for each vertex v in vertices: if v is source then distance[v] := 0 else distance[v] := infinity predecessor[v] := null // 步驟2:重復對每一條邊進行松弛操作 for i from 1 to size(vertices)-1: for each edge (u, v) with weight w in edges: if distance[u] + w < distance[v]: distance[v] := distance[u] + w predecessor[v] := u // 步驟3:檢查負權環 for each edge (u, v) with weight w in edges: if distance[u] + w < distance[v]: error "圖包含了負權環"
二、SPFA算法
求單源最短路的SPFA算法的全稱是:Shortest Path Faster Algorithm。 SPFA算法是西南交通大學段凡丁於1994年發表的。松弛操作必定只會發生在最短路徑前導節點松弛成功過的節點上,用一個隊列記錄松弛過的節點,可以避免了冗余計算。復雜度可以降低到O(kE),k是個比較小的系數(並且在絕大多數的圖中,k<=2,然而在一些精心構造的圖中可能會上升到很高)。
SPFA算法可以認為是對Bellman Ford算法的優化,但不論如何,這是在最短路徑問題中由中國學生提出的算法,算法的核心思路並不復雜,主要的操作就是維護一個candidate隊列,最開始的時候只有s,然后對s的鄰接邊進行松弛,如果可以松弛,則判斷一下當前結點是否在隊列中,如果不在的話,那么將該結點加入隊列,重復操作,知道隊列為空。
從理性的角度來分析這個算法的合理性:
只要最短路徑存在,上述SPFA算法必定能求出最小值。證明:每次將點放入隊尾,都是經過松弛操作達到的。換言之,每次的優化將會有某個點v的最短路徑估計值d[v]變小。所以算法的執行會使d越來越小。由於我們假定圖中不存在負權回路,所以每個結點都有最短路徑值。因此,算法不會無限執行下去,隨着d值的逐漸變小,直到到達最短路徑值時,算法結束,這時的最短路徑估計值就是對應結點的最短路徑值。
SPFA判環:
當一個結點入隊次數到達V的時候,表明其中有環。如同Bellman Ford算法,只需要V - 1次就可以完成所有的松弛,如果做了 V 次松弛,那么說明有環。
具體實現中可以用一個數組保存每個結點的入隊次數。
procedure Shortest-Path-Faster-Algorithm(G, s) 1 for each vertex v ≠ s in V(G) 2 d(v) := ∞ 3 d(s) := 0 4 offer s into Q 5 while Q is not empty 6 u := poll Q 7 for each edge (u, v) in E(G) 8 if d(u) + w(u, v) < d(v) then 9 d(v) := d(u) + w(u, v) 10 if v is not in Q then 11 offer v into Q