算法初接触 | 图的搜索[狄克斯特拉算法]

245 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天

狄克斯特拉算法

与前面提到的贝尔曼-福特算法类似,狄克斯特拉(Dijkstra)算法也是求解最短路径问题的算法,使用它可以求得从起点到终点的路径中权重总和最小的那条路径路径

边找候补顶点边选择路线(单向计算),顶点可到达的所有路线单向计算,选择权重最小的顶点留下,其他顶点直接忽略,最终留下权重最小的一条路

图解

01

1.jpg
A为起点,G为终点

02

2.jpg
首先设置各个顶点的初始权重:起点为0,其他顶点为无穷大(∞)

03

3.jpg
从起点出发

04

4.jpg
寻找可以从目前所在的顶点直达且尚未被搜索过的顶点,此处为顶点B和顶点C,将它们设为下一步的候补顶点

05

5.jpg
计算各个候补顶点的权重。计算方法是“目前所在顶点的权重+目前所在顶点到候补顶点的权重”。比如起点A的权重是0,那么顶点B的权重就是0+2=2。用同样的方法计算顶点C,结果就是0+5=5

06

6.jpg
如果计算结果小于候补顶点的值,就更新这个值。顶点B和顶点C现在的权重都是无穷大,大于计算结果,所以更新这两个顶点的值

07

7.jpg
从候补顶点中选出权重最小的顶点。此处B的权重最小,那么路径A-B就是从起点A到顶点B的最短路径。因为如果要走别的路径,那么必定会经过顶点C,其权重也就必定会高于A-B这条路径

08

8.jpg
确定了最短路径,移动到顶点B

09

9.jpg
将可以从顶点B直达的顶点设为新的候补顶点,于是顶点D和顶点E也成为了候补。目前有三个候补顶点C、D、E

10

10.jpg
用相同的方法计算各个候补顶点的权重。从B到C的权重为2+6=8,比C当前的权重5大,因此不更新这个值

11

11.jpg
更新了剩下的顶点D、E

12

12.jpg
从候补顶点中选出权重最小的顶点。此处D的权重最小,那么路径A-B-D就是从起点A到顶点D的最短路径

13

13.jpg
A-B-D 这条路径是通过逐步从候补顶点中选择权重最小的顶点来得到的,所以 如果经过其他顶点到达 D,其权重必定会大于这条路径

14

14.jpg
要重复执行同样的操作直到到达终点G为止。移动到顶点D后算出了E的权重,然而并不需要更新它(因为3+4=7)。现在,两个候补顶点C和E的权重都为5,所以选择哪一个都可以

15

15.jpg
此处选择了C,于是起点A到顶点C的最短路径便确定了

16

16.jpg
移动到C后,顶点F成为了新的候补顶点,且F的权重被更新为13。此时的候补顶点中,E为 5、F为13,所以······

17

17.jpg
我们选择了权重更小的E,起点A到顶点E的最短路径也就确定了下来

18

18.jpg
移动到E。G成了新的候补顶点,其权重也被更新为14。此时的候补顶点中,F为13、G为14,所以选择了F。由此,起点A到顶点F的最短路径也就确定了下来

19

19.jpg
移动到F。顶点G的权重计算结果为13+7=20,比现在的值14要大,因此不更新它。由于候补顶点只剩G了,所以选择G,并确定了起点A到顶点G的最短路径

20

20.jpg
到达终点G,搜索结束

21

21.jpg
用粗线条标注的就是从起点A到终点G的最短路径

解说
比起需要对所有的边都重复计算权重和更新权重的贝尔曼-福特算法,狄克斯特拉算法多了一步选择顶点的操作,这使得它在求最短路径上更为高效

补充说明
狄克斯特拉算法和贝尔曼-福特算法一样,也能在有向图中求解最短路径问题。
22.jpg
但是如果图中含有负数权重,狄克斯特拉算法可能会无法得出正确答案,这一点和贝尔曼-福特算法有所不同。比如右边这个图中,A-C-B-G为正确的最短路径,权重为4+(-3)+1=2。
然而,如果用狄克斯特拉算法来求解,得到的却是下面这样的最短路径树。从起点A到终点G的最短路径为A-B-G,权重为3。这个答案显然是错误的。

c6f31cfe47a7654e4ea21c36182fbf5.jpg
如果闭环中有负数权重,就不存在最短路径。贝尔曼-福特算法可以直接认定不存在最短路径,但在狄克斯特拉算法中,即便不存在最短路径,它也会算出一个错误的最短路径出来。因此,有负数权重时不能使用狄克斯特拉算法。

总的来说,就是不存在负数权重时,更适合使用效率较高的狄克斯特拉算法,而存在负数权重时,即便较为耗时,也应该使用可以得到正确答案的贝尔曼-福特算法