算法复习——图算法篇之单源最短路径之Bellman-Ford算法
以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!
1. 问题背景
在计算带权图中源点到所有其他顶点的最短路径时,如果图中存在负权边,Dijkstra算法不再适用。
那么图中存在负权边时,是否存在单源最短路径?
如果源点 s s s可达负环,则难以定义最短路径。因为可以绕负环,不断松弛到 − ∞ -∞ −∞。
若源点 s s s无可达负环,则存在源点 s s s的单源最短路径。
2. 问题定义
单源最短路径问题(Single Source Shortest Paths Problem)
输入:
- 带权图 G = < V , E , W > G=<V, E, W> G=<V,E,W>
- 源点编号 s s s
输出:
- 源点 s s s到所有其他顶点 t t t的最短距离 δ ( s , t ) \delta(s, t) δ(s,t)和最短路径 < s , … , t > <s, \dots, t> <s,…,t>
- 或存在源点 s s s可达的负环
挑战1:图中存在负权边时,如何求解单源最短路径?
挑战2:图中存在负权边时,如何发现源点可达负环?
3. 算法思想
- Dijkstra算法通过松弛操作迭代更新最短距离
- 存在负权边时,需要比Dijkstra算法更多次数的松弛操作
问题:图中存在负权边时,如何利用松弛操作求解单源最短路?
- Bellman-Ford算法
- 解决挑战1:图中存在负权边时,如何求解单源最短路径?
- 每轮对所有边进行松弛,持续迭代 ∣ V ∣ − 1 |V|-1 ∣V∣−1轮
- 解决挑战2:图中存在负权边时,如何发现源点可达负环?
- 若第 ∣ V ∣ |V| ∣V∣轮仍松弛成功,存在源点 s s s可达的负环
- 解决挑战1:图中存在负权边时,如何求解单源最短路径?
4. 伪代码
Bellman-Ford(G, s)
输入:图G=<V, E, W>,源点s
输出:单源最短路径P
新建一维数组dist[1..|V|],pred[1..|V|]
// 初始化
for u ∈ V do
dist[u] ← ∞
pred[u] ← NULL
end
dist[s] ← 0
// 执行单源最短路径算法
for i ← 1 to |V| - 1 do
for (u, v) ∈ E do
if dist[u] + w(u, v) < dist[v] then
dist[v] ← dist[u] + w(u, v)
pred[v] ← u
end
end
end
for (u, v) ∈ E do
if dist[u] + w(u, v) < dist[v] then
print 存在负环
break
end
end
该算法的时间复杂度是 O ( ∣ V ∣ ∗ ∣ E ∣ ) O(|V|*|E|) O(∣V∣∗∣E∣)。
5. 算法正确性证明
- 挑战1:图中存在负权边时,如何求解单源最短路径?
- 解决方案:每轮对所有边进行松弛,持续迭代 ∣ V ∣ − 1 |V|-1 ∣V∣−1轮
- 最坏情况
- 非环路的路径 < s , v 2 , v 3 , … , v ∣ V ∣ > <s, v_2, v_3, \dots, v_{|V|}> <s,v2,v3,…,v∣V∣>至多经过 ∣ V ∣ − 1 |V|-1 ∣V∣−1条边
- 最坏情况下进行 ∣ V ∣ − 1 |V|-1 ∣V∣−1轮松弛操作,可以保证求得单源最短路径
- 挑战2:图中存在负权边时,如何发现源点可达负环?
- 解决方案:若第 ∣ V ∣ |V| ∣V∣轮仍松弛成功,存在源点 s s s可达负环
- 若源点 s s s可达负环,可松弛成功无限次