ruye07

导航

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⁴

posted on 2026-05-16 15:39  ruye07  阅读(15)  评论(0)    收藏  举报