1. 程式人生 > >透徹理解迪傑斯特拉演算法

透徹理解迪傑斯特拉演算法

Dijkstra(迪傑斯特拉)演算法是典型的單源最短路徑演算法,這個演算法我主動學了三遍,第一主動學的時候,是看嚴蔚敏的《資料結構》,當時應該是學懂了,有點透徹地理解了這個演算法,但是沒有記錄下來,後來就忘記了, 第二次主動學,就去網上找相關文章,看了不少關於這個演算法的講解,但總感覺都沒有講透,看得我二懂二懂的,昨天晚上,突然又想到這個演算法,發現我還是不熟悉這個演算法,又忘記Dijkstra 演算法是怎麼一回事了,決定再看這個演算法一遍,雖然已經快12點了,平時這個時候已經躺床上了。 這次終於徹底搞懂,並決定寫成部落格記錄下來。

Dijkstra 演算法,用於對有權圖進行搜尋,找出圖中兩點的最短距離,既不是DFS搜尋,也不是BFS搜尋。
把Dijkstra 演算法應用於無權圖,或者所有邊的權都相等的圖,Dijkstra 演算法等同於BFS搜尋。

http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html

2.演算法描述
1)演算法思想:設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以後每求得一條最短路徑 , 就將加入到集合S中,直到全部頂點都加入到S中,演算法就結束了),第二組為其餘未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當前最短路徑長度。

例子
先給出一個無向圖
用Dijkstra演算法找出以A為起點的單源最短路徑步驟如下

不要看演算法的動畫,理解演算法的時候,思維更不上GIF動畫的速度,這兩張圖對理解演算法最管用
重點需要理解這句拗口的”按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度

實際上,Dijkstra 演算法是一個排序過程,就上面的例子來說,是根據A到圖中其餘點的最短路徑長度進行排序,路徑越短越先被找到,路徑越長越靠後才能被找到,要找A到F的最短路徑,我們依次找到了
A –> C 的最短路徑 3
A –> C –> B 的最短路徑 5
A –> C –> D 的最短路徑 6
A –> C –> E 的最短路徑 7
A –> C –> D –> F 的最短路徑 9
Dijkstra 演算法執行的附加效果是得到了另一個資訊,A到C的路徑最短,其次是A到B, A到D, A到E, A到F

為什麼Dijkstra 演算法不適用於帶負權的圖?
就上個例子來說,當把一個點選入集合S時,就意味著已經找到了從A到這個點的最短路徑,比如第二步,把C點選入集合S,這時已經找到A到C的最短路徑了,但是如果圖中存在負權邊,就不能再這樣說了。舉個例子,假設有一個點Z,Z只與A和C有連線,從A到Z的權為50,從Z到C的權為-49,現在A到C的最短路徑顯然是A –> Z –> C

對帶負權的圖,應該用Floyd演算法

再舉一例, 初點a到b最短有權10,b到c有權50,a到d有權30,d到c有權20,求a到c的最短路徑。

迪傑斯特拉演算法的執行過程是一個排序的過程,既不是深度優先也不是廣度優先演算法。 就這個簡單的例子而言,a和b都被加入S集合後,這時下一個要加入S集合結點,並不一定是c節點。 請把a和b節點組成的集合作為一個整體來考慮,當演算法執行到 a和b 都被加入S集合後,你甚至都可以把 a 和 b在圖中去掉用一個虛擬的s節點來表示,a 和 b 作為一個整體後,和這個整體相連線的邊,最短的就是 a 到 d 這條邊,但是,這時並不能直接因為這條邊的權最小 就將d 加入 S 集合; 現在,再來假設 b 到 c的權不是50,而是25; 現在和 S 集合相連線的邊有a到d 30, b到c 25; 這時,b到c的權值最小,但卻不能加入S集合, 因為 a 到 b 再到 c的權是 10 + 25 已經大於 a 到 d的權 30, 所以 d 應該加入 S 集合,而不是 c; 如果b 到 c的權值小於20,就是 c先加入 S 集合,而不是 d 了; b 到 c的權值恰好等於20,那麼隨便,先把d加入 S 還是 先把 c 加入 S,都是一樣的,沒有區別。

另外,任意一點k,加入s集合後,就已經找到了從源點a到k的最短路徑,前提條件是圖中不能有帶負數的權值邊。 我這樣表述可以讓演算法更清楚,還是這個例子, a和b都被選入 S 集合後,去更新整個圖,把a和b都去掉,用新節點 s 代替a和b,s 到 d 有權30, s 到 c 有權 10+50, 下一步該選哪個節點併入 S 集合一目瞭然了吧。 d點選入 S 集合後,再把d點從圖中抹去,更新虛擬的節點 s到c的權

Dijkstra(迪傑斯特拉)演算法是典型的單源最短路徑演算法,這個演算法我主動學了三遍,第一主動學的時候,是看嚴蔚敏的《資料結構》,當時應該是學懂了,有點透徹地理解了這個演算法,但是沒有記錄下來,後來就忘記了, 第二次主動學,就去網上找相關文章,看了不少關於這個演算法的講解,但總感覺都沒有講透,看得我二懂二懂的,昨天晚上,突然又想到這個演算法,發現我還是不熟悉這個演算法,又忘記Dijkstra 演算法是怎麼一回事了,決定再看這個演算法一遍,雖然已經快12點了,平時這個時候已經躺床上了。 這次終於徹底搞懂,並決定寫成部落格記錄下來。