1. 程式人生 > >最短路徑(二)—Dijkstra演算法(通過邊實現鬆弛:鄰接矩陣)

最短路徑(二)—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較大的圖稱為稠密圖

),我們可以用鄰接表來代替鄰接矩陣儲存,使得整個時間複雜度優化到O(M+N)logN。

在最壞的情況下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演算法下是無法更新的,由此便可能得不到正確的結果。