狄克斯特拉算法
- 什么是狄克斯特拉算法
- 操作步骤
- Python 实现
- 负权边问题
什么是狄克斯特拉算法
狄克斯特拉(Dijkstra)算法用于在加权图中求最短路径。广度优先搜索找的是段数最少的路径(非加权图),狄克斯特拉算法找的是总权重最小的路径(加权图)。
核心思想:每次从未处理的节点中选出开销最小的,更新其邻居的开销。
操作步骤
以 A 为起点、G 为终点为例:
B --3-- E
| |
2 4
| |
A --6-- D --9-- G
| |
5 7
| |
C --8-- F
第 1 步:初始化开销表
A=0 B=∞ C=∞ D=∞ E=∞ F=∞ G=∞
第 2 步:从 A 出发,更新邻居的开销
B = 0 + 2 = 2
C = 0 + 5 = 5
开销表: A=0 B=2 C=5 D=∞ E=∞ F=∞ G=∞
第 3 步:选出开销最小且未处理的节点 B(2),确定 A→B 为最短路径,处理 B
B 的邻居: D 和 E
D = 2 + 1 = 3 < ∞ → 更新
E = 2 + 3 = 5 < ∞ → 更新
B→C: 2 + 6 = 8 > 5 → 不更新
开销表: A=0 B=2 C=5 D=3 E=5 F=∞ G=∞
第 4 步:选开销最小的未处理节点 D(3),确定 A→B→D 为最短路径
开销表: A=0 B=2 C=5 D=3 E=5 F=∞ G=∞
继续:选 C(5),F = 5+8=13 → 更新;选 E(5),G = 5+9=14 → 更新;选 F(13),F→G = 13+7=20 > 14 → 不更新;选 G(14),到达终点。
最终最短路径权重为 14。
Python 实现
graph = {}
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2
graph["a"] = {}
graph["a"]["fin"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["fin"] = 5
graph["fin"] = {}
infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None
processed = []
def find_lowest_cost_node(costs):
lowest_cost = float("inf")
lowest_cost_node = None
for node in costs:
cost = costs[node]
if cost < lowest_cost and node not in processed:
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
node = find_lowest_cost_node(costs)
while node is not None:
cost = costs[node]
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
if costs[n] > new_cost:
costs[n] = new_cost
parents[n] = node
processed.append(node)
node = find_lowest_cost_node(costs)
负权边问题
狄克斯特拉算法不能处理负权边。因为算法假设处理过的节点就是最短路径,但负权边可能使后续找到更短的路径,导致之前的假设不成立。
如果图中有负权边,应该使用贝尔曼-福特算法。
时间复杂度
- 不优化:O(n²)
- 用堆优化:O(m + n log n)
小结
- 狄克斯特拉算法每次选开销最小的节点处理,逐步确定最短路径
- 适用于加权图(无负权边),只适用于有向无环图(DAG)
- 不能处理负权边,有负权边用贝尔曼-福特
- 比贝尔曼-福特更高效