用最小堆優化 Dijkstra 算法

偷一份算法導論 dj 算法的僞代碼:

DIJKSTRA(G, w, s)
1  INITIALIZE-SINGLE-SOURCE(G, s)
2  S ← Ø
3  Q ← V[G]                                 //V*O14  while Q ≠ Ø
5      do u ← EXTRACT-MIN(Q)     //EXTRACT-MIN,V*O(V),V*O(lgV)
6         S ← S ∪{u}
7         for each vertex v ∈ Adj[u]
8             do RELAX(u, v, w)       //鬆弛技術,E*O1),E*O(lgV)。

當僅使用線性查找算法實現 EXTRACT-MIN 時,查找的時間複雜度爲 O(V),所以很垃圾,直接導致最後結果出現 O(V^2),這不太好。

有些人用最小堆(優先隊列)來實現 EXTRACT-MIN,企圖把 O(V^2) 降低到 O(VlogV) 會有一個問題:
當使用鬆弛技術時,會破壞最小堆的結構。有些人每次 EXTRACT-MIN 之前進行一次建堆操作,但這實際上又引入了 O(V) 的單次查找複雜度,比線性查找只慢不快。
所以我們需要一個算法把最小堆的結構恢復過來。這在 Q 中建立 elemindex 的映射,以便每次 RELAX 知道我們碰了誰,然後進行局部恢復,保持 O(logV) 的單次查找複雜度,但是這樣的缺點是需要用到優先隊列內部封裝的結構,而且維護索引的常數開銷也很大,特別是 Java PriorityQueue 這種輪子唾手可得時,我們不願意去自己寫一個最小堆(就是懶)
但是換個角度,我們可以這樣操作:

RELAX 後,立即將節點插入 Q 中。u = EXTRACT-MIN(Q) 後如果 uvisited,那麼跳過它。

這樣等價於 “每次鬆弛後維護了 Q 的結構”,缺點是 Q 會變得龐大起來,每次查找是 O(logE),考慮到 E <= V^2O(logE) ~ O(logV) ,還不算太壞。循環的次數也會增多,但是如果碰到 visited == true 就會很快進入下一循環,可以認爲O(logE)因子仍然是 O(V),因爲跳過循環時不進行 O(logE) 的循環內操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章