一文彻底搞懂Dijkstra算法
2026-07-06 19:05:41
发布于:浙江
一文彻底搞懂Dijkstra算法(迪杰斯特拉算法)
1. 算法本质
Dijkstra算法 是解决非负权图单源最短路径的经典贪心算法。
由荷兰计算机科学家 Edsger W. Dijkstra 于1956年提出。
核心思想:按路径长度递增的顺序,逐步确定从源点到其余各顶点的最短路径。
2. 适用场景与前提
- 适用:带权有向图或无向图,边权 ≥ 0。
- 不适用:含负权边的图(此时应使用 Bellman-Ford 或 SPFA)。
- 目标:求给定源点
s到所有其他顶点的最短距离。
3. 核心数据结构
dist[]:记录源点到每个顶点的当前最短距离估计值,初始化dist[s]=0,其余为∞。visited[](或settled[]):标记顶点是否已确定最短距离(永久标号)。- 优先队列(小顶堆):快速取出当前
dist最小的未确定顶点,优化时间复杂度。
4. 算法步骤(手动模拟版)
- 初始化:
dist[s]=0,其余dist=∞,所有顶点未访问。 - 从未访问顶点中选择
dist最小的顶点u(初始为源点)。 - 标记
u为已访问(确定其最短距离)。 - 松弛操作:遍历
u的所有邻接边(u, v, w),若dist[u] + w < dist[v],则更新dist[v] = dist[u] + w。 - 重复步骤 2~4,直到所有顶点被访问或剩余
dist全为∞(非连通图)。
5. 图解示例(手动推演)
图:
顶点:A(源点), B, C, D
边:A-B(4), A-C(2), B-C(1), B-D(5), C-D(8)
| 步骤 | 已确定顶点 | dist[A] | dist[B] | dist[C] | dist[D] | 选择 |
|---|---|---|---|---|---|---|
| 初始 | 无 | 0 | ∞ | ∞ | ∞ | A |
| 1 | A | 0 | 4 | 2 | ∞ | C |
| 2 | A,C | 0 | 3(4→2+1) | 2 | 10(2+8) | B |
| 3 | A,C,B | 0 | 3 | 2 | 8(3+5) | D |
| 结束 | 全部 | 0 | 3 | 2 | 8 | - |
最终最短路径:
A→C(2), A→B(3) [A→C→B], A→D(8) [A→C→B→D]
6. 代码实现(Python + 堆优化)
import heapq
def dijkstra(graph, start):
# graph: {节点: [(邻接节点, 权重), ...]}
dist = {node: float('inf') for node in graph}
dist[start] = 0
pq = [(0, start)] # (当前距离, 节点)
visited = set()
while pq:
d, u = heapq.heappop(pq)
if u in visited:
continue
visited.add(u)
for v, w in graph[u]:
if v not in visited and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
heapq.heappush(pq, (dist[v], v))
return dist
# 示例图(无向)
graph = {
'A': [('B', 4), ('C', 2)],
'B': [('A', 4), ('C', 1), ('D', 5)],
'C': [('A', 2), ('B', 1), ('D', 8)],
'D': [('B', 5), ('C', 8)]
}
print(dijkstra(graph, 'A')) # {'A':0, 'B':3, 'C':2, 'D':8}
7. 复杂度分析
| 实现方式 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 邻接矩阵 + 朴素 | O(V²) | O(V²) |
| 邻接表 + 二叉堆 | O((V+E) log V) | O(V+E) |
| 邻接表 + 斐波那契堆 | O(E + V log V) | O(V+E) |
V:顶点数,E:边数。优先队列堆优化是实际中最常用方式。
8. 正确性证明(简要)
- 不变式:每次从
visited中取出的顶点u,其dist[u]已是全局最小值。 - 反证法:若存在更短路径到达
u,则该路径上第一个未访问顶点x的dist[x]必小于dist[u],与u是当前最小矛盾。 - 由于所有边权非负,后续路径不可能使
dist[u]更小,故贪心选择安全。
9. 常见误区与注意事项
- 不能处理负权边:负权会使贪心失效(可能绕负权环减小距离)。
- 不一定适用于所有最短路变种:如求最长路、含负权图、多源最短路等。
- 图必须连通吗? 不必须,非连通部分距离保持
∞。 - 堆中可能出现旧数据:用
visited或比较d == dist[u]来跳过过期条目。
10. 路径还原(附加)
若需要输出具体最短路径,维护 parent[] 数组,在松弛时记录 parent[v] = u,最后反向追溯。
def dijkstra_with_path(graph, start):
dist = {node: float('inf') for node in graph}
parent = {node: None for node in graph}
dist[start] = 0
pq = [(0, start)]
visited = set()
while pq:
d, u = heapq.heappop(pq)
if u in visited:
continue
visited.add(u)
for v, w in graph[u]:
if v not in visited and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
parent[v] = u
heapq.heappush(pq, (dist[v], v))
return dist, parent
# 使用示例
dist, parent = dijkstra_with_path(graph, 'A')
# 追溯 A -> D 的路径
path = []
cur = 'D'
while cur is not None:
path.append(cur)
cur = parent[cur]
path.reverse()
print(path) # ['A', 'C', 'B', 'D']
11. 总结
| 特点 | 说明 |
|---|---|
| 算法类型 | 贪心算法 |
| 适用图 | 非负权有向图/无向图 |
| 时间复杂度 | O((V+E) log V)(堆优化) |
| 空间复杂度 | O(V+E) |
| 稳定性 | 稳定,无负权环时必得正确解 |
| 典型应用 | 地图导航、网络路由、任务调度等 |
一句话记忆:
每次从「未确定」的顶点中挑一个「当前最近」的,然后用它去「松弛」邻居,直到所有顶点都确定下来。
这里空空如也

















有帮助,赞一个