最短路徑(二)—Dijkstra演算法(通過邊實現鬆弛:鄰接矩陣)
上一節通過Floyd-Warshall演算法寫了多源節點最短路徑問題:
這一節來學習指定一個點(源點)到其餘各個頂點的最短路徑。也叫做“單源最短路徑”Dijkstra。
例如求下圖中1號頂點到2、3、4、5、6號頂點的最短路徑。
用二維陣列e儲存頂點之間邊的關係,初始值如下:
用一個一維陣列dis來儲存1號頂點到其餘各個頂點的初始路程,
此時dis陣列中的值稱為最短路程的“估計值”。
先找一個離1號頂點最近的頂點,是2號頂點,當選擇了2號頂點後,dis[2]的值就已經 從“估計值”變為了“確定值”。接下來看2號頂點的出邊,有2-3和2-4兩條邊。先討論通過2-3能否讓1-3號的路程變短,也就是比較dis[3]和dis[2]+e[2][3]的大小。2-4同理。
我們對2號頂點所有出邊進行了鬆弛,完畢後dis陣列為:
接下來,繼續在剩下的3、4、5、6頂點中,選注離1號最近的頂點,是4號,4號的所有出邊4-3、4-5、4-6鬆弛:
。。。
Dijkstra的主要思想:每次找到離源點最近的一個頂點,然後以該頂點為中心進行擴充套件,最終得到源點到其餘所有點的最短路徑。
步驟:
輸入資料:
執行結果:
這個時間複雜度是O(N^2)。
其中每次找到離1號頂點最近的頂點的時間複雜度是O(N),這裡可以用“堆”來優化使降低到O(logN),
另外對於邊數M少於N^2的稀疏圖來說(M<<N^2的圖稱為稀疏圖,而M較大的圖稱為稠密圖
在最壞的情況下M就是N^2,這樣的話(M+N)logN要比N^2還要大,但是大多數情況下並不會有那麼多邊,因此(M+N)logN要比N^2小很多。
DijkStra基於貪心策略的演算法。
對於不含負權邊的圖求單源最短路徑,Dijkstra演算法是最高效的。但是在含負權邊的圖中,Dijkstra很可能得不到正確的結果,因為Dijkstra每次選的是當前能連到的邊中權值最小的,在正權圖中這種貪心是對的,但是在負權圖中就不是這樣了。比如1——>2權值為5,1——>3權值為6,3——>2權值為-2,求1到2的最短路徑時,Dijkstra就會選擇權為5的1——>2,但實際上1——>3——>2才是最優的結果。
另外如果包含負環,則意味著最短路徑不存在。因為只要在負權迴路上不斷兜圈子,所得的最短路長度可以任意小。
原因:
Dijkstra演算法當中將節點分為已求得最短路徑的集合(記為P)和未確定最短路徑的集合(記為Q),歸入P集合的節點的最短路徑及其長度不再變更,如果邊上的權值允許為負值,那麼有可能出現當與P內某點(記為a)以負邊相連的點(記為b)確定其最短路徑時,它的最短路徑長度加上這條負邊的權值結果小於a原先確定的最短路徑長度,而此時a在Dijkstra演算法下是無法更新的,由此便可能得不到正確的結果。