Dijkstra 算法——计算有权最短路径(边有权值)
【0】README
0.1) 本文總結于 數據結構與算法分析, 源代碼均為原創, 旨在理解 Dijkstra 的思想并用源代碼加以實現;
0.2)最短路徑算法的基礎知識,參見 http://blog.csdn.net/pacosonswjtu/article/details/49894021
0.3) Dijkstra算法 涉及到的 優先隊列的操作實現(該優先隊列的數據類型不是 int , 而是 Distance),詳情參見
http://blog.csdn.net/pacosonswjtu/article/details/49923389
0.4)Floyd算法(弗洛伊德算法)求的是: 兩個頂點間的最短路徑, 這個可以用Dijkstra算法來實現,因為 Dijkstra求的是某個頂點到其他頂點的最短路徑,當然也就包括了 Floyd算法的求解情況;
0.5)所有點對最短路徑
- 0.5.1)有時重要的是要找出 圖中所有頂點對之間的最短路徑。 雖然我們可以運行|V| 次適當的單源算法, 但是如果要立即計算所有的信息, 我們還是期望有更快的算法, 特別是對稠密圖的求解;
- 0.5.2) 在第10章中, 我們將看到對賦權圖求解這種問題的 一個 O(|V|^3)算法。雖然對于稠密圖,它具有和運行 |V| 次簡單 Dijkstra 算法相同的時間界, 但是循環是如此地緊湊以至于所有專門的點對算法很可能在實踐中更快。當然,對于稀疏圖更快的是運行 |V| 次用優先隊列編寫的 Dijkstra算法;
【1】Dijkstra 算法相關
1.1)貪婪算法一般分階段去求解一個問題, 在每個階段它都把當前出現的當做是最好的去處理:
- 1.1.1)貪婪算法荔枝(使用最少數目的紙幣找零錢):
說找零錢, 大部分人首先數出面值1元的紙幣,然后是面值5角的紙幣、2角的紙幣、1角的紙幣等等;這種貪婪算法使用最少數目的紙幣找零錢; - 1.1.2)貪婪算法的主要問題: 該算法不能總是成功,為了找還15角的零錢,如添加面值1元2角的紙幣(這僅僅是舉例說明)可破壞這種找零錢算法, 因為此時它給出的答案(一個面值1元2角的紙幣+1個面值2角的紙幣+一個面值1角的紙幣==3個)不是最優的(1個面值1元的紙幣+1個面值5角的紙幣==2個);
1.2)Dijkstra 算法:解決單源最短路徑問題的一般方法叫做 Dijkstra算法, 它的解法是貪婪算法最好的例子;
- 1.2.1) Dijkstra 算法像無權最短路徑算法一樣, 按階段進行;在每個階段, 該算法選擇一個頂點v, 它在所有未知頂點中具有最小的dv, 同時算法聲明從s到v的最短路徑是已知的。階段的其余工作由dw值的更新工作組成;
- 1.2.2)利用反證法證明得到, 只要沒有邊的值為負, 該算法總能夠順利完成,如果任何一邊出現負值, 則算法可能得出錯誤的答案;
- 1.2.3) Dijkstra算法描述(轉自天勤計算機考研高分筆記——數據結構)
設有兩個頂點集合S 和 T, 集合S中存放圖中已找到最短路徑的頂點,集合T存放圖中剩余頂點。初始狀態時, 集合S 中只包含源點V0, 然后不斷從集合T中選取到頂點V0 路徑長度最短的頂點Vu 并將其并入到集合S中。集合S每并入一個新的頂點Vu, 都要修改頂點V0到 集合T中頂點的最短路徑長度值。不斷重復這個過程, 直到集合T的頂點全部并入到 S中為止;
Attention)在理解“集合S每并入一個新的頂點Vu,都要修改頂點V0到集合T中頂點的最短路徑長度值”的時候需要注意:
- A1)在Vu被選入S中后, Vu被確定為最短路徑上的頂點, 此時Vu就像V0到達T中頂點的中轉站 ,多了一個中轉站, 就會多一些達到T中頂點的新路徑,而這些新路徑有可能比之前V0到T中頂點的路徑還要短,因此需要修改原有V0到T中其他頂點的路徑長度。此時對于T中的一個頂點Vk, 有兩種情況:一種是V0不經過Vu 到達Vk的路徑長度為a, 另一個是V0經過Vu到達Vk的長度為b。 如果a<=b, 則什么也不做;如果 a>b , 則用b來代替a。 用同樣的方法處理T中其他頂點, 當T中所有頂點都被處理完后, 會出現一組新的 V0到T中各個頂點的路徑,這些路徑中有一條最短的, 對應了T中一個頂點, 就是新的 Vu, 將其并入S。重復上述過程, 最后T中所有的頂點都會被并入到S中, 此時就可以得到 V0到圖中所有頂點的最短路徑;
【2】Dijkstra算法實現
2.1)圖是稠密的: 通過使用掃描表來找出最小值dv, 那么每一步將花費 O(|V|)時間找到最小值, 從而整個算法過程將花費 O(|V|^2)時間查找最小值;每次更新dw的時間是常數, 而每條邊最多有一次更新,總計為 O(|E|),因此總的運行時間為
O(|E| + |V|)=O(|V|^2);
2.2)圖是稀疏的:邊數 |E|=Θ(|V|) , 那么掃描法就太慢了,不適用于稀疏圖;
- 2.2.1)一種處理方法是把更新處理成 DecreaseKey 操作: 此時, 查找最小值的時間為 O(log|V|), 即為執行那些更新的時間, 它相當于執行那些 DecreaseKey操作的時間。由此得出運行時間為 O(|E|log|V| + |V|log|V|)=O(|E|log|V|),它是對前面稀疏圖的界的改進;由于優先隊列不是有效地支持 Find操作, 由此 di 的每個值在優先隊列的位置將需要保留并當 di 在優先隊列中改變時更新。如果優先隊列使用二叉堆實現的 話,那么將會很難辦;如果使用配對堆(pairing heap, 見第12章),則程序不會太差;
- 2.2.2)另一種方法是在每次執行第9行時把w和新值dw插入到優先隊列中去。(這里僅僅提供了一個idea,可以不去細究,因為Solution多種多樣)這樣,在優先隊列中的每個頂點就可能有多于一個的代表。當 DeleteMin操作吧最小的頂點從優先隊列中刪除時, 必須檢查以肯定它不是已經知道的。這種方法雖然從軟件觀點來看是優越的,而且編程容易得多,但是,隊列的大小可能達到 |E| 那么大。由于|E| <= |V|^2 意味著 log|E| <=2log|V| , 因此這并不影響漸進時間界。這樣,我們仍然得到一個O(|E|log|V|)算法。不過,空間需求的確增加了, 在某些應用中這可能是嚴重的。不僅如此, 因為該方法需要 |E| 次而不僅僅是 |V| 次 DeleteMin, 所以在實踐中運行很慢;
- 2.2.3)圖在大多數情況下都是非常稀疏的:注意,對于一些諸如計算機郵件和大型公交傳輸的典型問題, 它們的圖都是非常稀疏的, 因為大多數頂點只有少數幾條邊。因此,在許多應用中 使用優先隊列來解決這種問題 是很重要的;
- 2.2.4)使用斐波那契堆實現 Dijkstra算法, 如果使用不同的數據結構,那么 Dijkstra算法可能會有更好的時間界。我們將看到另外的優先隊列數據結構,叫做斐波那契堆(Fibonacci heap)。使用這種數據結構的運行時間為 O(|E| + |V|log|V|)。斐波那契堆具有良好的 理論時間界,不過,它需要相當數量的系統開銷。因此,尚不清楚在實踐中是否使用 斐波那契堆比使用具有二叉堆的Dijkstra 算法更好;
【3】看個荔枝:
【4】source code + printing results
Attention)
- A1)代碼的打印結果 和 手動模擬結果做個比較,以驗證我的代碼可行性: 注意將我的打印結果和章節【3】中的“有權最短路徑Dijkstra算法步驟解析”中的各個步驟的binary heap 和 table內容(存儲在進行Dijkstra算法過程中的節點相關數據)做個比較,很直觀地演示了 Dijkstra算法的步驟;
- A2)出現的問題: 本源代碼用到了 優先隊列(二叉堆)來選取最小的 distance所在的vertex編號,很方便,不過有個問題就是,當起始頂點(我們這里是v1)到后面的鄰接頂點比之前的鄰接頂點還要小(章節【3】中的v3被聲明為已知后,v6的Distance更新為8,就是這種情況),那么就需要更新優先隊列里面的v6的distance(由9更新為8),但是優先隊列對于 find 操作不是很有效。
- A3)如何解決優先隊列對find操作不是很有效的情況: 這里, 我們引入了另一個int類型的數組indexOfVertexInHeap who stores index of vertexs in heap and let every element be -1 initially;比如,v6存放在 heap的第5個位置上,那么 indexOfVertexInHeap[6]=5,對的,就是這樣sample, 后面,我們需要更新 heap里面的某個vertex的distance,直接用 indexOfVertexInHeap 導出該vertex在heap中的位置,然后直接更新就可以了,Bingo!
4.1)download source code:
Dijkstra算法源代碼(優先隊列實現):https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p228_dijkstra
4.2)source code at a glance(for complete code, please click given link above):
4.3)printing results:
總結
以上是生活随笔為你收集整理的Dijkstra 算法——计算有权最短路径(边有权值)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手机怎么设置iis(手机怎么设置无线路由
- 下一篇: 打印结果和调试结果不一样(C语言)