最短路径
1. 从导航到网络路由——最短路问题的普适性
给定一个带权图,求两顶点之间总权重最小的路径——这是计算机科学中最基本也最实用的问题之一。
- GPS 导航:Dijkstra 算法在道路网络中计算最短路线
- 网络路由:OSPF 协议使用 Dijkstra 计算最短路径树
- 拼写纠错:编辑距离可以建模为网格图上的最短路径
- 运筹优化:物流配送、航班调度都可以转化为最短路问题
2. Dijkstra 算法
2.1 核心思想(贪心)
Dijkstra 算法解决非负权图上的单源最短路径问题。核心思想是贪心扩展——每次从未确定最短距离的顶点中选出距离最小的,并尝试用它的边松弛邻居。
2.2 松弛操作
**松弛(Relaxation)**是核心概念:对边
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w2.3 算法步骤
- 初始化
dist[start] = 0,其他顶点dist = INF - 维护一个优先队列(最小堆),将所有顶点按 dist 值排序
- 每次弹出
dist最小的顶点 - 对
的每条出边 进行松弛操作 - 重复步骤 3-4 直到优先队列为空
2.4 复杂度分析
- 朴素实现(用数组扫描找最小值):
,适合稠密图 - 堆优化(用优先队列):
,适合稀疏图
2.5 正确性直觉
Dijkstra 算法为什么正确?关键在于所有边的权重都是非负的。当我们从优先队列中弹出顶点
2.6 局限性
Dijkstra 算法不能处理负权边。如果图中存在负权边,已经"确定"的最短距离可能被后续更短的负权路径推翻。

3. Bellman-Ford 算法
3.1 为什么需要 Bellman-Ford?
Dijkstra 遇到负权边就失效了。Bellman-Ford 算法可以处理任意带权图(只要没有负环),并且能够检测负环。
3.2 算法原理(动态规划视角)
Bellman-Ford 重复执行对所有边的松弛操作。第 dist[v] 的值就是从起点到
最多需要
3.3 算法步骤
- 初始化
dist[start] = 0,其他dist = INF - 重复
次:遍历所有边,对每条边进行松弛 - 额外做一轮松弛:如果还能更新任何
dist,说明存在负环
3.4 复杂度
时间复杂度
3.5 检测负环
第 V 轮:如果还能松弛任何边 → 存在负权环为什么?因为经过
4. SPFA 算法
4.1 队列优化的 Bellman-Ford
SPFA(Shortest Path Faster Algorithm)是 Bellman-Ford 的一个队列优化版本。观察到:只有上一轮被松弛了的顶点,其出边才可能在下一轮导致其他顶点的松弛。
4.2 算法步骤
- 初始化
dist[start] = 0,其他dist = INF - 维护一个队列,初始只有
start - 每次从队列中取出顶点
- 对
的每条出边 进行松弛:若 ,更新 并将 入队(若 不在队中) - 重复直到队列为空
4.3 SPFA 什么时候会退化?
SPFA 在绝大多数随机图上的表现优于 Bellman-Ford(接近
- Dijkstra 是非负权图的首选
- Bellman-Ford / SPFA 用于有负权边的场景
- 对于包含负环检测需求的场景,直接用 Bellman-Ford 更安全

5. Floyd-Warshall 算法
5.1 全源最短路径(All-Pairs Shortest Path)
前面三个算法都解决单源最短路径。如果需要所有顶点对之间的最短距离,朴素方法是对每个顶点都跑一遍 Dijkstra(
5.2 动态规划公式
F-W 算法的核心是考虑:对于任意两点
其中
通过滚动数组优化,可以压缩到二维:
for k in range(V):
for i in range(V):
for j in range(V):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])5.3 三重循环的顺序为什么是 k-i-j?
k 必须在最外层!这保证了我们使用的是「只允许经过前 i 或 j 放在最外层,会错误地使用「允许经过前
6. 其他最短路径算法一览
| 算法 | 单源/全源 | 权重要求 | 复杂度 | 特点 |
|---|---|---|---|---|
| BFS | 单源 | 无权(权重=1) | 最简单 | |
| Dijkstra | 单源 | 非负 | 贪心,最常用 | |
| Bellman-Ford | 单源 | 任意(可检测负环) | DP 思想 | |
| SPFA | 单源 | 任意 | B-F 的队列优化 | |
| Floyd-Warshall | 全源 | 任意 | DP,简洁优雅 | |
| Johnson | 全源 | 任意 | 结合 B-F + Dijkstra | |
| A* | 单源到单目标 | 非负 | 启发式 | 有启发函数时最有效 |
6.1 Johnson 算法(直觉)
Johnson 算法使用**重赋权(reweighting)**技术,让有负权边的图变成非负权图,然后对每个顶点跑 Dijkstra。核心技巧是用 Bellman-Ford 计算一个势函数
6.2 A* 搜索
A* 在 Dijkstra 的基础上引入了启发式函数
- 若
,A* 退化为 Dijkstra - 若
可采纳( 真实最小代价),A* 保证找到最优解 - 若
一致(满足三角不等式 ),A* 不会重复处理节点
A* 在游戏 AI 寻路、机器人路径规划中广泛应用。

7. 算法选择决策树
图中是否有负权边?
├── 否 → 是否单源?
│ ├── 是 → Dijkstra(堆优化) O((V+E)log V)
│ └── 否 → Floyd-Warshall V<500? → O(V³);否则 V*Dijkstra
└── 是 → 是否需要检测负环?
├── 是 → Bellman-Ford O(VE)
└── 否 → SPFA(平均快但可能退化)
本章总结
最短路径是图论中最核心的问题之一,五种经典算法各有适用场景:
- Dijkstra:非负权图的首选,堆优化后
- Bellman-Ford:支持负权边并检测负环,
- SPFA:B-F 的队列优化,平均
但可能退化 - Floyd-Warshall:全源最短路径,
,DP 公式极简 - A*:引入启发式,在有信息的搜索空间中远超 Dijkstra
📥 Code
| File | View | Download |
|---|---|---|
| demo.py | Open | Download |
| exercise.py | Open | Download |
参考
- Dijkstra, E. W. (1959). A note on two problems in connexion with graphs. Numerische Mathematik.
- Bellman, R. (1958). On a routing problem. Quarterly of Applied Mathematics.
- Floyd, R. W. (1962). Algorithm 97: Shortest path. CACM.
- Hart, P. E., Nilsson, N. J., & Raphael, B. (1968). A formal basis for the heuristic determination of minimum cost paths.