算法初接触 | 图的搜索[贝尔曼-福特算法]

192 阅读4分钟

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

贝尔曼-福特算法

贝尔曼一福特(Belman-Ford)算法是一种在图中求解最短路径问题的算法。最短路径问题就是在加权图指定了起点和终点的前提下,寻找从起点到终点的路径中权重总和最小的那条路径

双向计算,数字小的覆盖数字大的

图解

01

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

02

2.jpg
首先设置各个顶点的初始权重:起点为0,其他顶点为无穷大(∞)。这个权重表示的是从A到该顶点的最短路径的暂定距离。随着计算往下进行,这个值会变得越来越小,最终收敛到正确的数值

03

3.jpg
从所有的边中选出一条边,此处选择了连接A-B的边。然后,分别计算这条边从一端到另一端的权重,计算方法是“顶点原本的权重+边的权重”。只要按顺序分别计算两个方向的权重即可,从哪一端开始都没有问题。此处我们选择按顶点权重从小到大的方向开始计算

04

4.jpg
A的权重小于B,因此先计算从A到B的权重。A的权重是0,边A-B的权重是9,因此A到B的权重是0+9=9

05

5.jpg
如果计算结果小于顶点的值,就更新这个值。顶点B的权重是无穷大,比9大,所以把它更新为9。更新时需要记录计算的是从哪个顶点到该顶点的路径

06

6.jpg
接下来计算从B到A的权重。B的权重为9,从B到A的权重便为9+9=18。与顶点A现在的值0进行比较,因为现在的值更小,所以不更新

07

7.jpg
对所有的边都执行同样的操作。在执行顺序上没有特定要求,此处我们选择从靠近左侧的边开始计算。先选出一条边……

08

8.jpg
数值更新,顶点C的权重变成了2

09

9.jpg
同样的,再选出一条边......

10

10.jpg
权重更新。此时看出,从顶点A前往顶点B时,比起A直达B,在C中转一次的权重更小

11

11.jpg
接着对所有边进行更新操作

12

12.jpg
更新边B-D,B-E

13

13.jpg

14

14.jpg
更新边C-D,C-F

15

15.jpg
第2轮更新也结束了。顶点B的权重从8变成了7,顶点E的权重从9变成了8。接着,再执行一次更新操作。更新完所有的边后,第1轮更新就结束了。接着,重复对所有边的更新操作,直到权重不能被更新为止

16

16.jpg
第3轮更新结束,所有顶点的权重都不再更新,操作到此为止。算法的搜索流程也就此结束,我们找到了从起点到其余各个顶点的最短路径

17

17.jpg
根据搜索结果可知,从起点A到终点G的最短路径是A-C-D-F-G,权重为14。

解说
将图的顶点数设为n、边数设为m,我们来思考一下贝尔曼一福特算法的时间复杂度是多少。该算法经过》轮更新操作后就会停止,而在每轮更新操作中都需要对各个边进行1次确认,因此1轮更新所花费的时间就是O(m),整体的时间复杂度就是O(mm)。
为了便于说明,前面的讲解以无向图为例,但在有向图中同样可以求解最短路径问题。选出一条边并计算顶点的权重时,无向图中的计算如前文步骤03-8所示,两个方向都要计算,而在有向图中只按照边所指向的那个方向来计算就可以了。

补充说明
计算最短路径时,边的权重代表的通常都是时间、距离或者路费等,因此基本都是非负数 。不过,即便权重为负,贝尔曼-福特算法也可以正常运行。 但是,如果在一个闭环中边的权重总和是负数,那么只要不断遍历这个闭环,路径的权重就能不断减小,也就是说根本不存在最短路径。遇到这种对顶点进行n次更新操作后仍能继续更新的情况,就可以直接认定它“不存在最短路径”