最短路径之贝尔曼-福特算法

基本概念算法

图:ide

最短路径之贝尔曼-福特算法

有顶点和边组成。又分为3d

有向图:code

最短路径之贝尔曼-福特算法

在这里只能从A到B,不能从B到A。blog

无向图:队列

最短路径之贝尔曼-福特算法

能从A到B,也能从B到A,也能够用下图表示:it

最短路径之贝尔曼-福特算法

还有就是给边加上权重,变成加权图:class

最短路径之贝尔曼-福特算法

权重表明了两个顶点链接的程度,它能够是时间、距离、路费等等,根据实际状况而定。搜索

最短路径:循环

最短路径之贝尔曼-福特算法

如上图,从A到D,有三种路径:ABD、AD、ACD。

考虑到边的权重(好比路费),三条线路中最短路径不是两点直连的AD(10),而是ABD(2+3=5)。

负环路:

虽然从现实场景中,人们很难想象边的权重是负数——没据说过走高速从A到B,不用交高速费,还倒找钱的。

可是从理论上来讲,仍是要考虑负权边的存在,这就致使一个问题:负环路的存在致使没法找出最短路径。

看下图:

最短路径之贝尔曼-福特算法

从A到A,花费的是0,这是最短路径了,可是由于有了负权边的存在,会形成:

A-B-C-A,权重为2+2-5=-1,也就是说A绕了一圈,变成-1,比0小,最短路径是ABCA了。

这还没完,再绕一圈,-1+2+2-5=-2,A变成了-2,照此循环下去,A到A的权重会愈来愈小。

这就是负环路,永远找不到最短路径。

固然,有负权边不表明必定有负环路,以下图:

最短路径之贝尔曼-福特算法

这就没有造成负环路,A到C的最短路径就是ABC=3

广度搜索优先:

简单地说,就是从根节点开始,搜索完其子节点后,再搜索子节点的子节点,直至找到目标节点或全部节点都被遍历一遍。

最短路径之贝尔曼-福特算法

如上图,将根节点A的子节点BCD放入队列,取出B,再将B的子节点EF放入队列,接着取出C,再将C的子节点G放入队列,按照队列先进先出的特性,遍历全部节点。

深度搜索优先:

从根节点开始,搜索完一条分支后,再搜索另外一分支。

最短路径之贝尔曼-福特算法

如上图,取根节点A压入栈。

取出A,获取A的子节点BCD,压入栈。

取出B,获取B的子节点EF,压入栈。

取出F,并没有子节点,且不是目标节点,抛出。E同理。

取出C,获取C的子节点G,压入栈。

取出G,同F。

取出D,获取D的子节点H,压入栈。

取出H,同F。

松弛操做:

最短路径之贝尔曼-福特算法

如上图,算出A到D的最短路径。

一开始咱们只知道A到A的路径是0,到BCD的路径未知,就设为∞。

计算A-D路径为0+10=10 <∞,故将D由∞改成10。这就是一次松弛操做。

贝尔曼-福特算法

简单地说,就是对图中全部订单、全部边都进行松弛操做,直到找到最短路径。因此其时间复杂度应该是O(顶点数*边数)。

伪代码应该是:

for(int i=0;i<顶点数-1;i++){
 for(int j=0;j<边数;j++){
  松弛操做;
 }
}

但在实际状况中,在小于“顶点数-1”次的遍历中,已经求出了最短路径,因此在内部循环结束后,校验一下有没有进行松弛操做,若是没有,则说明已求出最短路径,直接跳出便可。

伪代码:

for(int i=0;i<顶点数-1;i++){
 是否进行了松弛操做=false;
 for(int j=0;j<边数;j++){
  if(终点权重>起点权重+边权重){
   松弛操做:终点权重=起点权重+边权重;终点的起点设为起点名称;
   是否进行了松弛操做=true;
  }
 }
 if(没有进行松弛操做){
  break;
 }
}

再结合以前谈到的负环路,在执行完最多顶点数-1次循环后,理应获得最短路径,若是咱们额外再遍历一次全部的边,看看有没有进行松弛操做。若是有,说明存在负环路。

添加伪代码:

是否存在负环路=false;
for(int j=0;j<边数;j++){
 if(终点权重>起点权重+边权重){
  是否存在负环路=true;
  break;  
 }
}

操做步骤:

最短路径之贝尔曼-福特算法

如上图,共有5个顶点:ABCDE。

16条边(无向图,每条线表明两个边):AB、AC、AD、BA、BC、BE、CA、CB、CD、CE、DA、DC、DE、EB、EC、ED

以A为起点,计算到其余顶点的最短路径。

初始状态下,A的权重应为0,其余节点皆为∞。

一、处理AB,B的权重改成1。此时A=0,B=1,其他为∞。B的起点为A。

二、处理AC,C的权重改成7。此时A=0,B=1,C=7。C的起点为A。

三、处理AD,D的权重改成6。此时A=0,B=1,C=7,D=6。D的起点为A。

四、处理BA,BA=1+1=2>0,无须进行松弛操做。

五、处理BC,BC=1+1=2<7,C的权重改成2,此时A=0,B=1,C=2,D=6。C的起点改成B。

六、接着继续处理,本次对全部边的循环,得出以下结果:

A到各点最低消耗:A=0,B=1,C=2,D=6,E=4

各点的起点:B<--A,C<--B,D<--A,E<--C。

七、接着开启下一轮对全部边的循环,在这次循环中,没有进行松弛操做,故跳出循环。

八、判断没有负环路,至此得出最终结果。

相关文章
相关标签/搜索