Dijkstra算法模拟演示
目录
- Dijkstra算法简介
- 算法步骤
- 示例图
- 算法执行过程
- 总结
1. Dijkstra算法简介
- 用途:计算从单个源点到所有其他节点的最短路径
- 特点:适用于权重非负的有向或无向图
- 核心思想:每次选择具有最小暂时距离的未访问节点,更新其邻居的距离
2. 算法步骤
-
初始化:
- 设置起始节点的距离为0,其余节点的距离为∞(无穷大)
- 所有节点的前驱设为
None
- 建立一个未访问节点的集合
-
迭代:
-
从未访问节点中选取距离最小的节点
u
-
将节点
u
标记为已访问 -
对于
u
的所有邻居节点v
:-
如果
v
未访问,且通过u
到v
的距离小于当前v
的距离:- 更新
v
的距离为dist[u] + weight(u, v)
- 设置
v
的前驱为u
- 更新
-
-
-
重复步骤2,直到所有节点都被访问
3. 示例图
假设有以下加权无向图:
(此处需自行绘制或插入示例图)
-
节点集合:
A
,B
,C
,D
,E
-
边及权重:
A - B
:6A - D
:1B - C
:5B - D
:2B - E
:2C - E
:5D - E
:1
-
起始节点:
A
4. 算法执行过程
初始化
节点 | 距离 (dist ) | 前驱 (prev ) | 访问状态 |
---|---|---|---|
A | 0 | None | 未访问 |
B | ∞ | None | 未访问 |
C | ∞ | None | 未访问 |
D | ∞ | None | 未访问 |
E | ∞ | None | 未访问 |
步骤1
-
选择节点:距离最小的未访问节点
A
-
标记:
A
设为已访问 -
更新邻居:
-
节点
B
:- 新距离:
0 + 6 = 6
(原距离为∞) - 更新
dist[B] = 6
,prev[B] = A
- 新距离:
-
节点
D
:- 新距离:
0 + 1 = 1
(原距离为∞) - 更新
dist[D] = 1
,prev[D] = A
- 新距离:
-
步骤2
-
选择节点:距离最小的未访问节点
D
(dist[D] = 1
) -
标记:
D
设为已访问 -
更新邻居:
-
节点
B
:- 新距离:
1 + 2 = 3
(原距离为6) - 更新
dist[B] = 3
,prev[B] = D
- 新距离:
-
节点
E
:- 新距离:
1 + 1 = 2
(原距离为∞) - 更新
dist[E] = 2
,prev[E] = D
- 新距离:
-
步骤3
-
选择节点:距离最小的未访问节点
E
(dist[E] = 2
) -
标记:
E
设为已访问 -
更新邻居:
-
节点
B
:- 新距离:
2 + 2 = 4
(原距离为3) - 不更新(因为4 > 3)
- 新距离:
-
节点
C
:- 新距离:
2 + 5 = 7
(原距离为∞) - 更新
dist[C] = 7
,prev[C] = E
- 新距离:
-
步骤4
-
选择节点:距离最小的未访问节点
B
(dist[B] = 3
) -
标记:
B
设为已访问 -
更新邻居:
-
节点
C
:- 新距离:
3 + 5 = 8
(原距离为7) - 不更新(因为8 > 7)
- 新距离:
-
步骤5
- 选择节点:距离最小的未访问节点
C
(dist[C] = 7
) - 标记:
C
设为已访问
结果汇总
节点 | 距离 (dist ) | 前驱 (prev ) | 访问状态 |
---|---|---|---|
A | 0 | None | 已访问 |
B | 3 | D | 已访问 |
C | 7 | E | 已访问 |
D | 1 | A | 已访问 |
E | 2 | D | 已访问 |
5. 总结
-
最短路径:
- 从
A
到B
:A -> D -> B
,距离为3 - 从
A
到C
:A -> D -> E -> C
,距离为7 - 从
A
到D
:A -> D
,距离为1 - 从
A
到E
:A -> D -> E
,距离为2
- 从
-
路径重建:根据前驱节点
prev
字段,可以反向追溯最短路径
当然可以!下面我将详细证明Dijkstra算法的正确性。
证明:Dijkstra算法的正确性
Dijkstra算法用于解决单源最短路径问题,即在一个加权有向图中,找到给定起点到所有其他节点的最短路径。该算法假设所有边的权重都是非负数。以下是对该算法正确性的详细证明。
1. 问题描述
- 给定:一个加权有向图 ( G(V, E) ),其中:
- ( V ) 是节点集合。
- ( E ) 是边集合,每条边 ( (u, v) ) 具有非负权重 ( w(u, v) \geq 0 )。
- 目标:找到从起始节点 ( s ) 到所有其他节点 ( v \in V ) 的最短路径,即最小的路径权重和。
2. 算法描述
Dijkstra算法主要步骤:
-
初始化:
- 对所有节点 ( v \in V ),设置距离 ( dist[v] = \infty ),前驱节点 ( prev[v] = \text{undefined} )。
- 对起始节点 ( s ),设置 ( dist[s] = 0 )。
- 初始化一个未访问节点集合 ( Q = V )。
-
迭代:
- 当 ( Q ) 非空时:
- 从 ( Q ) 中选取使 ( dist[u] ) 最小的节点 ( u )。
- 从 ( Q ) 中移除 ( u )。
- 对 ( u ) 的所有邻居 ( v ):
- 如果 ( dist[v] > dist[u] + w(u, v) ),则更新:
- ( dist[v] = dist[u] + w(u, v) )。
- ( prev[v] = u )。
- 如果 ( dist[v] > dist[u] + w(u, v) ),则更新:
- 当 ( Q ) 非空时:
-
结果:
- 对于每个节点 ( v \in V ),最短路径长度为 ( dist[v] ),路径可通过 ( prev[v] ) 追溯。
3. 正确性证明
目标
证明:对于每个节点 ( v \in V ),在算法终止时,( dist[v] ) 等于从 ( s ) 到 ( v ) 的最短路径长度。
思路
- 使用 数学归纳法 和 反证法。
- 关键是证明在每次迭代中,被选取的节点 ( u ) 的 ( dist[u] ) 等于从 ( s ) 到 ( u ) 的最短路径长度。
详细证明
-
基础情况
- 初始时,( dist[s] = 0 ),显然是正确的。
- 对于其他节点 ( v \neq s ),初始 ( dist[v] = \infty )。
-
归纳假设
- 假设在某一步之前,已确定的节点集合 ( S ) 中,对所有 ( u \in S ),( dist[u] ) 等于从 ( s ) 到 ( u ) 的最短路径长度。
- 我们要证明,在下一步中选择的节点 ( u ),其 ( dist[u] ) 也满足上述性质。
-
选择最小 ( dist ) 的节点 ( u )
- 由于 ( u ) 是在未访问节点集合 ( Q ) 中具有最小 ( dist ) 值的节点。
- 我们需要证明:从 ( s ) 到 ( u ) 的任何路径,其长度不可能小于 ( dist[u] )。
-
反证法
- 假设:存在一条从 ( s ) 到 ( u ) 的路径 ( P ),其权重总和小于 ( dist[u] )。
- 这条路径必须经过若干节点,我们可以将其划分为两部分:
- 从 ( s ) 到 ( x ) 的子路径,其中 ( x \in S )(已访问节点)。
- 从 ( x ) 到 ( u ) 的边或路径,其中 ( x ) 的后继节点在 ( Q ) 中。
- 根据归纳假设,( dist[x] ) 是从 ( s ) 到 ( x ) 的最短路径长度。
- 因此,从 ( s ) 到 ( u ) 的路径长度应为 ( dist[x] + \text{路径从 } x \text{ 到 } u \text{ 的权重} )。
-
矛盾
- 由于 ( dist[u] ) 在上一轮未被更新为更小的值,说明:
- ( dist[u] \leq dist[x] + w(x, u) )。
- 但根据假设,存在更短的路径使得:
- ( dist[u] > dist[x] + w(x, u) )。
- 这产生了矛盾。
- 由于 ( dist[u] ) 在上一轮未被更新为更小的值,说明:
-
结论
- 假设不成立,因此,不存在比 ( dist[u] ) 更短的路径。
- 所以,当节点 ( u ) 被选中并移出 ( Q ) 时,( dist[u] ) 就是从 ( s ) 到 ( u ) 的最短路径长度。
关键性质
- 贪心选择性质:每次选择未访问节点中距离最小的节点,其当前路径即为最短路径。
- 三角不等式:由于边的权重非负,因此不会存在通过其他路径比当前已知路径更短的情况。
4. 边权重非负的必要性
- 如果图中存在负权重的边,可能会导致算法错误。
- 例如,已确定的最短路径可能被后续经过负权重边的路径所更新。
5. 示例验证
- 在实际运行中,Dijkstra算法的更新过程符合上述性质。
- 可以通过具体例子来验证算法在每一步的正确性。
6. 结论
- 通过数学归纳和反证法,我们证明了Dijkstra算法能够正确计算从起始节点到所有其他节点的最短路径长度。
- 关键在于:
- 每次选择的节点,其 ( dist ) 值即为最短路径长度。
- 已确定的最短路径不会被后续更新。
- 边的非负权重保证了贪心选择的正确性。
希望这个证明能帮助你理解Dijkstra算法的正确性。如有任何疑问,欢迎提问!