单源最短路径之贝尔曼福特算法(Bellman-ford)及其队列优化算法SPFA算法
作者:互联网
一、概述
贝尔曼-福特算法(Bellman–Ford),是求解单源最短路径(也就是找到从一个节点到图上其他所有节点的最短路径)问题的一种算法,由理查德·贝尔曼和莱斯特·福特创立。它的原理是对图进行次松弛操作,得到所有可能的最短路径。
常常拿它与Dijkstra算法作对比。Dijkstra算法也是用于求解单源最短路径的,但是当图中存在负权弧时,该算法就不适用了。而Bellman-ford算法恰恰可以解决这个问题。然而,Bellman-ford算法的时间复杂度过高,因此就有一些相应的优化算法来提高它的效率(SPFA算法)。
二、为什么Dijkstra不可以求解存在负权弧的问题?
如果图中存在负权环,Dijkstra算法在求解时就会陷入循环,因为它总能找到一个更短的路径。
让我们来举一个例子:
参考: link.(特别好的视频,是英文但是强烈推荐!)
1、负的自循环边
负权环出现的一种方式是负的自循环边,一旦陷入负的自循环,就会在这个负循环里循环无数次直到退出。而其他经过这个循环后可到达的点的最短路径就会变成负无穷大(点2,3,4,5)。
2、总权值为负
由几个点组成的环的总权值为负(把该环中所有的边上的权值相加为负),经过这个循环可以到达的节点的最短路径为负无穷大(3,4,5)。
三、Bellman-ford算法的具体步骤
假设有n个点,m条边,s是起点。
1、定义一个距离数组d[n],除起点对应的d[i]初始化为0外,其余均初始化为正无穷。该数组用于记录从起点到各点的距离。(最后d[n]里面记录的就是从起点到各点的最短距离)
2、对d[n]进行n-1次的更新(因为在有n个点的情况下,最坏的情况,也就是到达最后一个点经过的边数最多的情况,共需要经过n-1条边)。
3、每一次更新时,找本次可以进行更新的点进行操作(但每条弧都要访问到)。在访问弧时,假设弧的起点为u,终点为v,权值为w,要看d[u]+w是否小于d[v],若满足,则更新d[v]为d[u]+w。
四、一种Bellman-ford算法的简单优化方式
在n-1次迭代中,若在第k次迭代时,d[n]不发生任何变化,则从这次开始,以后的每次迭代d[n]都不会再发生变化,也即已经求出了最短路径。可以通过插旗帜的方式来进行优化(即设一变量flag,并对该变量进行判断)。
五、Bellman-ford的队列优化算法——SPFA算法
上述Bellman-ford算法在每次迭代寻找能更新的值的时候,是很盲目的,每一次都会把m条边全部检查一遍。但其实每次并不需要去检查以上一次距离没有变化的点为起点的边(因为即便检查也不会满足更新条件)。因此,我们可以从这个角度出发,对Bellman-ford算法进行优化,这也就是SPFA算法。
我研究这个算法是为了求解“最小费用最大流问题”,因此并没有把这三种方式单独实现一遍,而是用SPFA直接编写了“最小费用最大流”的求解程序。所以这里就只整理它们的思路和原理啦~
标签:路径,单源,Bellman,ford,算法,循环,贝尔曼,SPFA 来源: https://blog.csdn.net/Chelsea_n/article/details/106817309