用最小堆优化 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) 的循环内操作。

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/weixin_38587349/article/details/80645992
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞