最短路算法:Bellman-Ford,SPFA

什么??你问我为什么不在一篇文章写完所有方法??
Hmm…其实我是想的,但是博皮的加载速度再带上文章超长图片超多的话…
可能这辈子都打不开了吧…

上一篇:Floyed and dijkstra

福特算法(Bellman-Ford)

适用范围及时间复杂度

单源最短路径算法,可处理负边权,但,无法处理负回路的情况。时间复杂度O(NE)

N:顶点数,E:边数

核心思想

松弛计算。什么是松弛计算?你戳…

……

好吧我说…

松弛计算之前,点B的值是8,但点A的值加上边上的权重2,得到点B的值是5,5<8,更新B为5。这个过程的意义是:找到了一条通向B更短的路线,且该路线先经过A,然后通过权重为2的边,到达点B。

当然,也会存在另一种情况:

在进行松弛计算之前,点B的值是6,点A加上点边上的权值4得到7>6,表示没有找到通向B点更短的路线,故B不需要更新。
这也就诠释了为什么算法不可以处理负权回路的情况。

使用松弛之后,发现程序陷入死循环。

代码实现

不难发现,在使用算法前,我们要去检验负权回路。我们需要不断地迭代以求解。

初始化

首先我们需要一个边集数组,暂定u表示起点,v表示终点,w表示权。而这个数组的长度为边的数量。

顶点数*(顶点数-1)/2。

什么?你不知道什么是边集数组?

 边集数组
  边集数组是利用一维数组存储图中所有边的一种图的表示方法。
  该数组中所存储的元素个数要大于等于图中所有的边数。
  每个元素用来存储图一条边起点,终点和权值。
  struct edge{int s,e;double w;}edges[maxn*(maxn-1)/2];
  特别注意的是:如果是无向图,则边的数量=顶点数量*(顶点数量-1)/2,加边函数为:
      void addEdges(int u,int v,double w){
      edges[++idx].s=u;edges[idx].e=v;edges[idx].w=w;
     edges[++idx].e=u;edges[idx].s=v;edges[idx].w=w;
 }
 而在枚举边的时候,需要枚举idx条边。
 定义边集数组时,特别要注意取值范围
 边集数组是针对边的,如果题目给定了边的数量为n
 ,那么在无向邻接矩阵中,边集数组要定义为2*n+1,
 必须考虑来回。如果题目只给定了顶点数p,没有给定边数,
 则边集数组的取值为:p*(p-1)/2

因为是无向图,算法第一层循环枚举点,第二层枚举边,所以加边函数的定义应是无向图的方法。

算法核心

void bellman(){
    while(--n){
        bool b=false;
        for(int j=1;j<=idx;j++){
            if(dis[edges[j].e]>dis[edges[j].s]+edges[j].w){
                dis[edges[j].e]=dis[edges[j].s]+edges[j].w;
                b=true;
            }
        }
        if(!b){
            break;
        }
    }
} 

 ----------7.22 Update

学个A*不知道扒出来了我多少学错了的东西…这篇里面提到了最基本的福特算法,但是有一个队列优化没有补充。我一直误以为SPFA是福特算法的别称,在这里做出纠正顺便优化一下之前乱七八糟的写法。

SPFA(队列优化的Bellman-Ford)

仅仅在大陆流行叫这个优化SPFA。优化过后,在随机数据的表现情况为O(KE),K是一个较小的常数。在特殊构造中,有可能被退化成O(NE)。

优化思想是:

1.建立一个队列,最初队列中只有一个起点1

2.取出队首节点x,扫描他的所有出边(x,y,z)。如果有Dis[y]>Dis[x]+z,则用后者更新前者。同时,若y不在队列中,将y入队

3.重复以上步骤,直至队列为空。

这个队列避免了福特算法中对不需要进行扩展的节点的冗余扫描。

void spfa()
{
    memset(Distance,0x3f,sizeof(Distance));
    memset(Vist,0,sizeof(Vist));
    Distance[1]=0;
    Vist[1]=1;
    q.push(1);
    while(q.size())
    {
        int x=q.front();
        q.pop();
        Vist[x]=0;
        for(int i=head[x]; i; i=Edges[i].Next)
        {
            int y=Edges[i].End,z=Edges[i].Val;
            if(Distance[y]>Distance[x]+z)
            {
                Distance[y]=Distance[x]+z;
                if(!Vist[y])
                {
                    q.push(y),Vist[y]=1;
                }
            }
        }
    }
}
原文地址:https://www.cnblogs.com/Uninstalllingyi/p/10421737.html