1. Dijkstra算法
在正数权重的有向图中求解某个源点到其余各个顶点的最短路径一般可以采用迪杰斯特拉算法(Dijkstra算法)。
1.1 适用场景
- 单源最短路径
- 权重都为正
1.2 伪代码
1.3 示例
问题描述: 计算节点0
到节点4
的最短路径,图路径如下:
step1: 采用二维表记录0点到其他节点的距离,第一列距离初始化为
∞
\infty
∞,第二列记录到达每个节点时,该节点前面的点,主要用于最短路径回溯。
step2: 0到0的距离是0,为最优路径,标记为对勾
,节点0到节点1、7的距离分别为4、8,节点1、7前面的点为节点0,所以在前面点填写0。
step3: 在未标记的点中选取最小值
,最小值为4,对应的节点为1,将其标记为最优路径,标记为对勾
,计算最小值节点1
的邻居节点。
经过最小值节点1
可以到达邻居节点2、7,到节点2的距离是
4
+
8
=
12
<
∞
4+8=12<\infty
4+8=12<∞,所以更新节点2的距离为12,到达节点2时,经过的前面节点为节点1。
经过最小值节点1
到达节点7的距离
4
+
11
=
15
>
8
4+11=15>8
4+11=15>8,所以不更新节点7,节点7前面的节点仍为节点0。
step4: 在剩余未标记的点中选取最小值
,最小值为8,对应的节点为7,将其标记为最优路径,标记为对勾
,计算最小值节点7
的邻居节点。
更新节点6、8,节点6的距离8(节点7的最短距离)+1=9
和节点8的距离8(节点7的最短距离)+7=15
,前面的节点都为7。以此类推,更新所有的节点。
最后0到所有节点的最短近距离如下:
step5: 节点0到节点4的最短距离为21,节点4的前面节点为节点5,
5
→
4
5 \rightarrow 4
5→4,节点5前面的节点是节点6,
6
→
5
6 \rightarrow5
6→5, 节点6前面是节点7,
7
→
6
7\rightarrow6
7→6,节点7前面是节点0,
0
→
7
0\rightarrow7
0→7,综上所述,最短路径为:
0
→
7
→
6
→
5
→
4
0 \rightarrow 7 \rightarrow6 \rightarrow 5 \rightarrow 4
0→7→6→5→4,距离为21。
2. Bellman-Ford算法
Bellman-ford算法适用于单源最短路径,图中边的权重可为负数即负权边,但不可以出现负权环。
2.1 适用场景
- 单源最短路径
- 边的权重可为负数即负权边
- 不可以出现负权环
2.2 伪代码
2.4 示例
step1: 初始图如下,箭头上的数字表示权重,括号内容含义为:(前面节点,距离)
,除去源点,其他点的初始距离为
∞
\infty
∞。
1
◯
−
7
◯
\textcircled{1}-\textcircled{7}
1◯−7◯表示边的编号。接下来,从节点0到节点n-1开始遍历。
step2: 从节点0
出发:更新所有节点的权重,节点
0
→
1
0 \rightarrow 1
0→1为90,
0
→
4
0 \rightarrow 4
0→4为75,
0
→
3
0 \rightarrow 3
0→3为80,它们的距离都小于
∞
\infty
∞,到达节点的上一个节点都是节点0,更新为(0,距离)。
step3: 从节点1
出发,
0
→
1
→
2
0 \rightarrow 1 \rightarrow 2
0→1→2为
90
−
30
=
60
<
∞
90-30=60<\infty
90−30=60<∞, 由于节点2上一个节点是节点1,所以标记为(1,60)。
step3: 从节点2
出发,60+10<75,节点4更新为(2,70)
step4: 从节点3
出发,80-30=50<60,更新节点2为(3,50)。80+10=90>70,无需更新节点4。
step4: 从节点4
出发,不存在边,无需更新,完成第一遍所有的松弛操作。
step5: 进行第二轮遍历,由于节点
2
→
4
2 \rightarrow 4
2→4 的和50+10=60<70,更新节点4为(2,60)。
step6: 第三遍遍历没有任何松弛操作,且3<=n-1=4,说明不存在负权重的环,可以直接返回节点0到其他节点的距离。如果存在松弛操作,最多进行n-1轮遍历,如果第n轮还存在松弛操作,说明存在负权重的环。
最短路径如下图红框所示:
如果存在负环,示例如下:
节点2、3、4存在负环,负环会导致无穷迭代。
第1遍遍历结果
更新节点4为(2,60)
更新节点3为(4,70)
更新节点2为(3,40),以此类推,导致无穷迭代。
3 Floyd算法(待完善)
4 算法特点对比(待完善)
Dijkstra 与Bellmon-ford
(1)Dijkstra为贪心算法,Bellmon-Ford算法不是贪心算法
(2)Dijkstra在有负权的情况下无法工作,Bellmon-Ford算法允许有负权。
(3)Bellmon-Ford算法可以用来判定是否有负权环。
(4)从计算复杂度角度分析,单节点算法首选Dijkstra算法的,它的计算复杂度为
O
(
n
2
)
O(n^2)
O(n2)
参考资料:
https://blog.csdn.net/weixin_41806489/article/details/126852955
https://www.bilibili.com/video/BV1zz4y1m7Nq
https://www.bilibili.com/video/BV18a4y1A7gv