算法八:Bellman-Ford算法及优化


一、Bellman-Ford算法

用于求最短路径,适用边的权值为负数或正数的(Dijkstra只适用于正权值)

1、算法方法:

n个顶点,m条边的图:
最多有n-1个循环,每个循环里对每一条边都执行松弛操作,此时就会有些顶点已经求得最短路,即这些顶点的最短路的“估计值”变成“确定值”。此后这些顶点的最短路的值就会一直保持不变,不再受后续松弛操作的影响。

一句话概括:对所有的边进行n-1次“松弛”操作,主要代码为4行:
for(int k=1;k<=n-1;k++)
   for(int i=1;i<=m;i++)
      if(dis[v[i]]>dis[u[i]]+w[i])
          dis[v[i]]=dis[u[i]]+w[i];

比如1点到2点的权值为3,那么判断dis[2]>dis[1]+3,如果大于,就把dis[2]置成dis[1]+3的值,就这样把m条边全部遍历一遍。

时间复杂度为O(NM),其实可以优化,就是在每次松弛完成后,检查dis数组是否有更新,没有更新就提前结束循环。

2、代码:

int i;
for(int k=1;k<=n-1;k++)
{
   for(i=1;i<=n;i++)
      bak[i]=dis[i];//添加一个一位数组来备份数组dis

   for(i=1;i<=m;i++)
      if(dis[v[i]]>dis[u[i]]+w[i])
          dis[v[i]]=dis[u[i]]+w[i];

   for(i=1;i<=n;i++)
      if(bak[i]!=dis[i]){
         check=1;
    break
       }
 
   if(check==0)
       break;
}

二、队列优化的Bellman-Ford算法

1、原理:

     只有那些在前一遍松弛中改变了最短路径估值的顶点,才能引起它们邻接点最短路径估计值发生变化。因此用一个对列来存放被成功松弛的顶点,之后只对队列中的点进行处理,这就降低了算法的时间复杂度。

2、算法方法:
每次仅对最短路估值发生变化了的顶点的所有边执行松弛操做;如果知道哪些点的最短路径发生了变化呢?可以用一个队列来维护。
struct
{
     int head;
     int tail;
     int que[m];
}

初始时将源点加入队列,每次从队首(head)取出一个顶点,并对与其相邻的所有顶点进行松弛尝试,若某个相邻的顶点松弛成功,且这个相邻的顶点不再队列中(不在head和tail之间),则将它加入队列中。对当前顶点处理完毕后立即出队,并对下一个新队首进行如上操作,直到队列为空时算法结束。
最坏的时间复杂度也是O(NM)。

国人段凡丁的《关于最短路径的SPFA快速算法》就是一种基于队列优化的Bellman-Ford算法。

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/my88site/article/details/46864111
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞