SPFA(bellman-ford的队列优化)

SPFA算法思想:
bellman-ford算法的时间复杂度较高,O(n^3)或者O(nm),原因在于算法要递推n次,且每次递推要扫描所有的边,并且在这个过程中很多遍是多余的.SPFA就是利用队列减少不必要的冗余判断。
SPFA的大致流程就是用一个队列来维护。开始时只有源点在队列里,从队列里取出一个顶点,并对与他相邻的所有顶点进行松弛,松弛成功则入队列。知道队列为空。
关于上面松弛成功,就代表着dis[v]变小了,所以所有通过顶点v的路径都可以变小,它具备了更新其他顶点的“资格”。而没有变小的顶点,去更新了也没有用,是冗余的。这样就去掉了冗余项了
SPFA算法,就是队列优化的bellman-ford,是根据“每个顶点的最短路径不会更新次数太多”的特点来优化的。SPFA算法可以在O(km)的时间里求出源点到其他顶点的最短路,并且可以处理负权值。k我每个顶点入队列的平均次数,通常情况k为2左右。
dis[i]存v0到i的距离,path[i]存i的“父顶点”。初始时dis[vo]=0,其他为INF,path[i]都为v0.并且v0入队列。
(1)取出队首的顶点u,扫描所有从u发出的边< u,v > , 如果dis[v]>dis[u]+w(u,v);则更新,并且如果v不在队列里面则将v入队列。
(2)执行1直到队列为空。

SFPA与BFS
SPFA和BFS的过程很像,不同之处在于BFS的出队列的顶点不会重新再次进入队列,而SPFA有重复进入的情况。

一个小例子:
给出n个顶点间的m条边,求出从原点0到其他顶点的所有最小权值,并输出相应的路径。
输入:
7 10
0 1 6
0 2 5
0 3 5
1 4 -1
2 1 -2
2 4 1
3 2 -2
3 5 -1
4 6 3
5 6 3

输出:
1: 1 0->3->2->1
2: 3 0->3->2
3: 5 0->3
4: 0 0->3->2->1->4
5: 4 0->3->5
6: 3 0->3->2->1->4->6

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn = 1010;
const int INF = 1000000;

int path[maxn];
int dis[maxn];
int inq[maxn];
int shortest[maxn];
int n,m;
queue<int> q;

struct Edge
{
    int to;
    int w;
};

vector<Edge> ve[maxn];


void SPFA(int v0)
{
    int u;
    memset(inq,0,sizeof(inq));
    for(int i=0;i<n;i++)
    {
        dis[i]=INF;
        path[i]=v0;
    }
    dis[v0]=0;
    while(!q.empty())
        q.pop();
    q.push(v0);
    inq[v0]++;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        inq[u]--;
        for(int i=0;i<ve[u].size();i++)
        {
            if(dis[ve[u][i].to]>dis[u]+ve[u][i].w)
            {
                dis[ve[u][i].to]=dis[u]+ve[u][i].w;
                path[ve[u][i].to]=u;
                if(!inq[ve[u][i].to])
                {
                    q.push(ve[u][i].to);
                    inq[ve[u][i].to]++;
                }
            }
        }
    }

}
int main()
{
    freopen("in.txt","r",stdin);
    int u,v,w;
    Edge e;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            e.to=v;e.w=w;
            ve[u].push_back(e);
        }
        SPFA(0);
        for(int i=1;i<n;i++)
        {
            printf("%d: %d ",i,dis[i]);
            memset(shortest,0,sizeof(shortest));
            int k=0;
            shortest[k]=i;
            while(path[shortest[k]]!=0)
            {
                k++;
                shortest[k]=path[shortest[k-1]];
            }
            shortest[++k]=0;
            for(int j=k;j>0;j--)
            {
                printf("%d->",shortest[j]);
            }
            printf("%d\n",shortest[0]);
        }

    }
    return 0;
}


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