堆优化Dijkstra算法针对Dijkstra算法中的第1步用堆进行优化,使用时需满足两个条件:
(1)边权没有负值
(2)稀疏图
存储结构采用邻接链表,n
为顶点数,m
为边数
int value[m], next[m], head[n], edge[m], d;
memset(head, -1, sizeof(head));
若源结点为顶点s
,dis[i]
标示源结点s
到顶点i
的最短距离(初始化dis[s]
为0
,其他为0x3f3f3f3f
),vis[i]
标示顶点i
是否已被访问(0
是未访问,1
是已访问,初始化为全0
)
int dis[n], vis[n];
堆优化Dijkstra算法基于贪心,算法流程如下:
(1)建立小顶堆,堆中元素为pair
类型,其first
为dis[j]
,second
为j
,取出堆顶,其为距离源结点s
最短的顶点p
(dis[p]
为最短),如果p
已被访问(vis[p]
为1
)需要舍弃
(2)标记p
为已访问(vis[p]
标记为1
),用顶点p
更新其可达顶点j
的dis[j]
(如果dis[p] + edge[i] < dis[j]
,则用dis[p] + edge[i]
更新dis[j]
,其中edge[i]
为顶点p
到顶点j
的边权),然后将pair
类型{dis[j], j}
入堆
(3)循环1、2步,不断取出堆顶直到堆空
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> heap;
heap.push({0, s});
while (!heap.empty()) {
auto t = heap.top();
heap.pop();
int p = t.second;
if (vis[p]) continue;
vis[p] = 1;
for (int i = head[p]; i != -1; i = next[i]) {
int j = value[i];
if (dis[p] + edge[i] < dis[j]) {
dis[j] = dis[p] + edge[i];
heap.push({dis[j], j});
}
}
}
算法结束时,dis[i]
存储源结点s
到顶点i
的最短距离,0x3f3f3f3f
表明不可达
注解:
(1)小顶堆heap
中的pair
为{dis[j], j}
,则先按dis[j]
升序排序,相等时再按j
升序排序
(2)heap
中的顶点不全是未访问,无法通过在heap
中判断是否访问(dis[j]
更新后顶点j
入堆,之前可能有j
旧值并未删除,后面j
新值在堆顶被取出并标记为已访问后,j
旧值仍然在堆中),故而第1步,如果p
已被访问需要舍弃
当然,值得一提的是,heap
中顶点j
的数据可能会存在多个,不过并不影响
(3)堆优化Dijkstra算法第2步,本应是用顶点t
更新其可达、未访问顶点j
的dis[j]
,可实现时更新操作却循环了所有可达顶点(这里面有已访问顶点),这是由于没有负权边,已访问顶点j的dis[j]
不会被更新,即使循环到也无妨
(4)堆优化Dijkstra算法适用于稀疏图(稀疏图定义参照图的存储结构)