1. 程式人生 > >單源最短路徑小結

單源最短路徑小結

all 均值 label str 標記 nbsp ast 容易 logs

一、負權問題

如果一個圖僅僅是存在負權,但不構成負權回路,又該如何?

技術分享

  • Dijkstra 算法

觀察上圖,若 A 作為源點,在第一輪循環後,B 被標記數組標記,但我們發現在第二輪循環中,B 還可以通過 C 點繼續進行更新。由此,可以得出結論:Dijkstra 算法不適用於負權圖。

  • Bellman_Ford 算法和 SPFA 算法

我們先思考下上述 “因 B 點被標記數組標記而導致無法通過 C 點再更新” 的問題,歸根結底是標記數組的鍋。有人提議,不妨去掉標記數組,如果去掉,就會造成很嚴重的問題,首當其沖的是“無法求出dist[]的最小值”。

反觀 Bellman_Ford 算法和 SPFA 算法,它們不存在標記數組的問題,因此對於僅僅存在負權的圖,它們都可以工作的很好。

最後,讀者需要註意的是,如果是無向圖,只要存在一條負邊,該圖就存在負權回路,這不難理解,無向圖的一條邊相當於有向圖的往返兩條邊。

二:Dijkstra,Bellman_Ford 和 SPFA,該用哪個?

技術分享

Bellman_Ford 沒什麽好說的,能不用就不用。

國際上一般不承認 SPFA 算法,因為在 SPFA 算法論文中對它的復雜度證明存在錯誤,其次 Bellman_Ford 的論文中已提及過這個隊列優化。

SPFA 算法有兩個優化策略 SLF 和 LLL。

  • SLF,Small Label First 策略,設要加入的節點是 j,隊首元素為 i,若dist[j] < dist[i]
    ,則將 j 插入隊首,否則插入隊尾;
  • LLL,Large Label Last 策略,設隊首元素為 i,隊列中所有 dist 值的平均值為 x,若dist[i] > x則將 i 插入到隊尾,查找下一元素,直到找到某一 i 使得dist[i] <= x,則將 i 出隊進行松弛操作。

SLF 可使速度提高 15 ~ 20%,SLF + LLL 可提高約 50%。 在實際的應用中 SPFA 的算法時間效率不是很穩定,為了避免最壞情況的出現,通常使用效率更加穩定的 Dijkstra 算法。

使用鄰接表 + 二叉堆優化的 Dijkstra 算法,復雜度適宜,也穩定,就是有個缺陷,不能處理負權回路。

最後話個嘮,我發現在算法競賽中我們大多數的選擇還是 SPFA,知乎了下,鄰接表 + 二叉堆優化的 Dijkstra 寫起來復雜,容易錯,而 SPFA 代碼簡單,容易寫,但是會被題目卡數據。

總結:首選 Dijkstra,其次 SPFA,最後 Bellman_Ford,具體采用哪種方法,視情況而定。

單源最短路徑小結