我在上一篇博客中讲解了狄克斯特拉算法,该算法可以用于寻找权值都为正的有向无环图的最短路径,我也提到了如果碰到权值为负的情况可以使用贝尔曼-福德算法,那么今天就让我们学习一下贝尔曼-福德算法是如何处理负权值和环路的。
其实理解了狄克斯特拉算法之后理解贝尔曼-福德算法就很容易了,如果说狄克斯特拉算法是用一种“广度”的方式,每次寻找源点所能到达的距离最短的一个点,更新其邻居节点的距离。那么贝尔曼-福德算法就是用一种“深度”的方式,每次找到所有的路径,进行“松弛”操作最终找到最短路径。贝尔曼-福德算法每次对所有的边进行松弛,每次松弛都会得到一条最短路径,所以总共需要要做的松弛操作是V - 1次。在完成这么多次松弛后如果还是可以松弛的话,那么就意味着,其中包含负环。相比狄克斯特拉算法其时间复杂度较高,达到O(V*E),V代表顶点数,E代表边数。
你可能不了解什么是松弛操作,其实就是不断寻找距离更短的路径:
假设有这样的一个图
从原点出发经由一条路径直达A的路径为M,直达B的路径为N,存在从A到B的路径L,如果你找到M+L<N的路径了,那么就是做了一次松弛操作
那么为什么说要做V-1次松弛操作呢?
考虑无环图的极限情况,所有节点排成一条线,那么最远的路段是V-1
那么遇到有环图呢?
最长路径便成了无限,但没经过一次环至少增加两短路,我们只要经过V-1次松弛便可以检测出来是否有环,对于有环的图我们还要对其性质进行判断,如果是正权值的环那么就排除改路径,如果是负权值的环那么此题就无法求解,因为走环的次数越多权值和越小。
如果你了解狄克斯特拉算法的话写这个也很容易,我们同样需要三个表一个距离表一个父节点表一个全部节点表,然后就是遍历V-1次,并判断是否存在环
我们找一个图 起始的样子是这样的
我们得到了一张从Start开始,第一步只能到达A、B两点的初始化的距离表,也就是说,此时到达A、B的距离是已知的,因为可以由Start直接到达; 得到一个只有Start点作为父节点的两个点A、B的父地点表;还有一个全部路径的路径图。
我们遍历路径表中的 相邻两点之间的全部路径 一次,根据距离表更新每个点的到达距离。也就是对图进行一次松弛操作:
我们可以看到,初始化