1. 程式人生 > >Dijkstra、Bellman-Ford及Spfa演算法思想對比

Dijkstra、Bellman-Ford及Spfa演算法思想對比

Dijkstra

dijkstra演算法本質上算是貪心的思想,每次在剩餘節點中找到離起點最近的節點放到佇列中,並用來更新剩下的節點的距離,再將它標記上表示已經找到到它的最短路徑,以後不用更新它了。這樣做的原因是到一個節點的最短路徑必然會經過比它離起點更近的節點,而如果一個節點的當前距離值比任何剩餘節點都小,那麼當前的距離值一定是最小的。(剩餘節點的距離值只能用當前剩餘節點來更新,因為求出了最短路的節點之前已經更新過了)
dijkstra就是這樣不斷從剩餘節點中拿出一個可以確定最短路徑的節點最終求得從起點到每個節點的最短距離。

Bellman-Ford

bellman-ford演算法進行n-1次更新(一次更新是指用所有節點進行一次鬆弛操作)來找到到所有節點的單源最短路。bellman-ford演算法和dijkstra其實有點相似,該演算法能夠保證每更新一次都能確定一個節點的最短路,但與dijkstra不同的是,並不知道是那個節點的最短路被確定了,只是知道比上次多確定一個,這樣進行n-1次更新後所有節點的最短路都確定了(源點的距離本來就是確定的)。
現在來說明為什麼每次更新都能多找到一個能確定最短路的節點:

1.將所有節點分為兩類:已知最短距離的節點和剩餘節點。

2.這兩類節點滿足這樣的性質:已知最短距離的節點的最短距離值都比剩餘節點的最短路值小。(這一點也和dijkstra一樣)

3.有了上面兩點說明,易知到剩餘節點的路徑一定會經過已知節點

4.而從已知節點連到剩餘節點的所有邊中的最小的那個邊,這條邊所更新後的剩餘節點就一定是確定的最短距離,從而就多找到了一個能確定最短距離的節點,不用知道它到底是哪個節點。

bellman-ford的一個優勢是可以用來判斷是否存在負環,在不存在負環的情況下,進行了n-1次所有邊的更新操作後每個節點的最短距離都確定了,再用所有邊去更新一次不會改變結果。而如果存在負環,最後再更新一次會改變結果。原因是之前是假定了起點的最短距離是確定的並且是最短的,而又負環的情況下這個假設不再成立。

Spfa

spfa可以看成是bellman-ford的佇列優化版本,正如在前面講到的,bellman每一輪用所有邊來進行鬆弛操作可以多確定一個點的最短路徑,但是用每次都把所有邊拿來鬆弛太浪費了,不難發現,只有那些已經確定了最短路徑的點所連出去的邊才是有效的,因為新確定的點一定要先通過已知(最短路徑的)節點。
這裡寫圖片描述
所以我們只需要把已知節點連出去的邊用來鬆弛就行了,但是問題是我們並不知道哪些點是已知節點,不過我們可以放寬一下條件,找哪些可能是已知節點的點,也就是之前鬆弛後更新的點,已知節點必然在這些點中。
所以spfa的做法就是把每次更新了的點放到佇列中記錄下來。

在實現上,bellman-ford可以用邊集陣列或鏈式前向星來實現。
而spfa則只能用鏈式前向星實現。

在效率上,當圖很稠密的時候spfa就退化成和bellman -ford差不多了,因為對於入隊的每個節點都要和很多節點去進行鬆弛操作。