Bellman-Ford 最短路径 学习笔记
Bellman-Ford 最短路径 学习笔记
学习日期: 2026-05-12
前置知识: Dijkstra(Dijkstra 怕负权,Bellman-Ford 不怕)
问题场景
Dijkstra 遇到负权边会翻车——已确定的最短距离后面可能被更短的推翻。Bellman-Ford 通过"全局多轮松弛"解决。
┌──(-1)──┐
1 ↓
│ 2
(2) │ │ (3)
↓ (1) ↓
3 ──────→ 4
Dijkstra 在这里:确定 3 是 2,但 1→2 是 -1 更短,无法回头改。
核心思想
做 n-1 轮:每轮检查所有边,看能不能缩小距离。
初始化: dist = [0, ∞, ∞, ∞] (dist[1]=0)
第1轮(松弛所有边):
边 1→2 权-1: dist[2] = min(∞, 0+(-1)) = -1
边 1→3 权2: dist[3] = min(∞, 0+2) = 2
边 2→4 权3: dist[4] = min(∞, -1+3) = 2
边 3→4 权1: dist[4] = min(2, 2+1) = 2 (没变)
dist = [0, -1, 2, 2]
第2轮:
全部没变 → 收敛!提前退出
最终: dist = [0, -1, 2, 2]
为什么最多 n-1 轮?
最坏情况:一条链 1→2→3→4→5
第1轮: 1→2 更新 ← 只推进1步
第2轮: 2→3 更新 ← 推进一步
第3轮: 3→4 更新
第4轮: 4→5 更新 ← n-1 = 4轮才传到底
没有负环时,最短路最多经过 n-1 条边。
每轮至少把信息往前推一步,所以 n-1 轮必收敛。
第n轮还能更新 = 有负环
负环: 2→3(权-2), 3→2(权-3),绕一圈 2→3→2 = -5
每绕一圈 dist 变小 → 永不收敛 → 第n轮还会更新
→ 检测到负环!
模板代码
def bellman_ford(n, edges, start):
INF = 10 ** 18
dist = [INF] * (n + 1)
dist[start] = 0
for i in range(n - 1): # n-1 轮
updated = False
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
updated = True
if not updated: # 提前收敛
break
# 第 n 轮检测负环
for u, v, w in edges:
if dist[u] != INF and dist[u] + w < dist[v]:
print("有负环")
return None
return dist
Dijkstra vs Bellman-Ford 对比
| 特性 | Dijkstra | Bellman-Ford |
|---|---|---|
| 负权边 | 不能 | 能 |
| 负环 | 不能 | 能检测 |
| 时间复杂度 | O(m log n) | O(n × m) |
| 空间复杂度 | O(n + m) | O(n + m) |
| 实现工具 | 堆 | 无(只需遍历边) |
| 适用 n, m | n ≤ 10⁵ | n ≤ 10³, m ≤ 10⁴ |
浙公网安备 33010602011771号