Bellman-Ford與SPFA


一、Bellman-Ford

Bellman-Ford 算法是一種用於計算帶權有向圖中單源最短路徑(當然也可以是無向圖)。與Dijkstra相比的優點是,也適合存在負權的圖。

若存在最短路(不含負環時),可用Bellman-Ford求出,若最短路不存在時,Bellman-Ford只能用來判斷是否存在負環。

松弛:  

  每次松弛操作實際上是對相鄰節點的訪問(相當於廣度優先搜索),第n次松弛操作保證了所有深度為n的路徑最短。由於圖的最短路徑最長不會經過超過|V| - 1條邊,所以可知貝爾曼-福特算法所得為最短路徑,也可只時間復雜度為O(VE)。

負邊權操作

  與迪科斯徹算法不同的是,迪科斯徹算法的基本操作“拓展”是在深度上尋路,而“松弛”操作則是在廣度上尋路,這就確定了貝爾曼-福特算法可以對負邊進行操作而不會影響結果。

負權環判定

  因為負權環可以無限制的降低總花費,所以如果發現第n次操作仍可降低花銷,就一定存在負權環

基本操作:

  1. 創建源頂點 v 到圖中所有頂點的距離的集合 distSet,為圖中的所有頂點指定一個距離值,初始均為 Infinite,源頂點距離為 0;
  2. 計算最短路徑,執行 V - 1 次遍歷;
    • 對於圖中的每條邊:如果起點 u 的距離 d 加上邊的權值 w 小於終點 v 的距離 d,則更新終點 v 的距離值 d;
  3. 檢測圖中是否有負權邊形成了環,遍歷圖中的所有邊,計算 u 至 v 的距離,如果對於 v 存在更小的距離,則說明存在環(無向圖不能用這種方法判斷負環)

正確性:

  Bellman-Ford 算法采用動態規划進行設計,實現的時間復雜度為 O(V*E),其中 V 為頂點數量,E 為邊的數量。簡單的說我們用

dis[k][v]表示經過前i個頂點到達v的最短路,易得轉移方程dis[k][v] = min(dis[k][v],dis[ k -1][u] + w)。未使用滾動數組優化空間時,實現的代碼如下:

 1 int dis[maxv][maxv];        //dis[k][v];表示選取前k個時到達i的最短距離
 2 struct Edge
 3 {
 4     int u, v, w;
 5 }edge[maxv];
 6 int n, m;
 7 
 8 void Bellman_Ford(int s)
 9 {
10     memset(dis, INF, sizeof(dis));
11     for (int i = 1; i <= n; i++)  dis[i][s] = 0;
12     for (int k = 1; k <= n - 1; k++)            
13         for (int i = 0; i < m; i++)                
14         {
15             int u = edge[i].u, v = edge[i].v, w = edge[i].w;
16             dis[k][v] = min(dis[k][v], dis[k - 1][u] + w);
17         }
18 }

 優化:

循環的提前退出:

  在實際操作中,貝爾曼-福特算法經常會在未達到 |V| - 1 次前就出解,|V| -1 其實是最大值。於是可以在循環中設置判定,在某次循環不再進行松弛時,直接退出循環,進行負權環判定。

 隊列優化:

  即SPFA

二、SPFA

是一個用於求解有向帶權圖單源最短路徑的改良的貝爾曼-福特算法(當然也可以通過將每條邊換為兩條逆向的邊來用於無向圖)。這一算法被認為在隨機的稀疏圖上表現出色,並且極其適合帶有負邊權的圖。然而SPFA在最壞情況的時間復雜度與Bellman-Ford算法相同,因此在非負邊權的圖中仍然最好使用Dijkstra。

原理:

  基於Bellman-Ford之外,再可以確定,松弛操作必定只會發生在最短路徑前導節點松弛成功過的節點上,用一個隊列記錄松弛過的節點,可以避免了冗余計算。

優化:

  SPFA算法的性能很大程度上取決於用於松弛其他節點的備選節點的順序。我們注意到其與Dijkstra很像,一方面,優先隊列替換成普通的FIFO隊列,而另一方面,一個節可以多次進入隊列點。

  事實上,如果 q 是一個優先隊列,則這個算法將極其類似於戴克斯特拉算法。然而盡管這一算法中並沒有用到優先隊列,仍有兩種可用的技巧可以用來提升隊列的質量,並且借此能夠提高平均性能(但仍無法提高最壞情況下的性能)。兩種技巧通過重新調整 q 中元素的順序從而使得更靠近源點的節點能夠被更早地處理。

距離小者優先(Small Lable First(SLF)):

  將總是把v壓入隊列尾端改為比較dis[v]與dis[q.front()]的大小(為了避免出現隊列為空的操作,先將v壓入隊尾),並且在v較小時將v壓入隊列的頭端。

距離大者優先(Large Lable Last(LLL)):

  我們更新隊列以確保隊列頭端的節點的距離總小於平均,並且任何距離大於平均的節點都將被移到隊列尾端。

  

 改為DFS版:

  dfs版spfa判環根據:若一個節點出現2次及以上,則存在負環。具有天然的優勢。由於是負環,所以無需像一般的spfa一樣初始化為極大的數,只需要初始化為0就夠了(可以減少大量的搜索,但要注意最開始時for一遍)。


免責聲明!

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



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