最短路算法总结
1. Dijkstra 算法
Dijkstra 算法的原理是贪心,执行步骤如下:
- 令 \(dis_s=0\),其余为正无穷;
- 在未被标记过的点中,选择 \(dis\) 最小的点 \(u\),标记它;
- 枚举 \(u\) 的出边,更新 \(v\) 的 \(dis\)。
重复步骤 2,3 直到所有点被标记。
在步骤 2 中找到的全局最小值,可以证明不会被再次更新了——其他的点 \(dis\) 已经大于 \(dis_u\),再加上若干条非负权边(Dijkstra 只能在非负权图上使用),显然无法更新 \(dis_u\)。
找全局最小值可以用堆优化,把时间复杂度降低到 \(O((m+n)\log n\)。代码实现
2. SPFA 算法
SPFA 由 Bellman-Ford 算法优化而来,首先介绍 Bellman-Ford 算法。
Bellman-Ford 算法基于三角形不等式,如果一组 \(dis\) 使得对于所有的边 \((u,v,w)\),都满足 \(dis_v \le dis_u + w\),则该组 \(dis\) 就是最短路——反证法,如果不是最短路,则必有一个 \(dis_v \ge dis_u + w\)。只要不断扫描边,进行更新,使得三角形不等式成立,就能得到最短路。时间复杂度 \(O(nm)\)。
SPFA 的计算步骤是:
- 初始 \(dis_s\) 入队;
- 取出队头 \(u\),枚举出边 \((u,v,w)\) 进行更新,如果 \(v\) 不在队中则进队;
重复 2 直到队列为空。SPFA 的时间复杂度是 \(O(km)\),\(k\) 一般是不大的常数,但可能被卡成 \(O(nm)\),例如 NOI2018 D1T1 归程。
SPFA 可以处理负权边,Dijkstra 不能处理负权边。代码实现
3. 双端队列 BFS
对于边权只有 0 和 1 的 01 最短路问题,我们可以使用双端队列 BFS \(O(n)\) 地求解。
如果边权全为 1,普通的 BFS 就能够解决问题:每个节点被扩展到的步数就是其最短路。当边权有 0 时,我们不需要“多迈一步”,可以使用双端队列,将其放在队头,当做同一层来处理;边权为 1 的,仍然放到队尾。

浙公网安备 33010602011771号