现在的位置: 首页 > 综合 > 正文

最短路SPFA学习资料(转载)

2017年07月15日 ⁄ 综合 ⁄ 共 2953字 ⁄ 字号 评论关闭

SPFA算法:求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm,是Bellman-Ford算法0(VE)的一个优化,期望的时间复杂度O(2E),E为图的边数,所以SPFA用在稀疏图上的效果会更加明显。SPFABellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变

很多时候,给定的图存在负权边,这时类似Dijkstra算法(0(V^2),在稠密图上有优势)便没有了用武之地,而bellman_ford的复杂度又过高,SPFA算法便派上用场了。


算法核心思想:理解这个算法,最好先看看Bellman-Ford,因为他是对Bellman-Ford的一个优化,SPFA算法采用了一个队列,优化算法,用数组dict[]记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。

 

需要注意的是:仅当图不存在负权回路时,SPFA能正常工作。如果图存在负权回路,由于负权回路上的顶点无法收敛,总有顶点在入队和出队往返,队列无法为空,这种情况下SPFA无法正常结束。

1 记录每个结点进队次数,超过|V|次表示有负权
2 记录这个结点在路径中处于的位置,ord[i],每次更新的时候ord[i]=ord[x]+1,若超过|V|则表示有负圈....

 

下面举一个实例来说明SFFA算法是怎样进行的:

 

    设有一个有向图G{VE},其中,V = {V0,V1,V2,V3,V4}E
=
{<V0,V1>,<V0,V4>,<V1,V2>,<V1,V4>,<V2,V3>,<V3,V4>,<V4,V2>} = {2,10,3,7,4,5,6},见下图:

 

最短路SPFA学习资料(转载)

 

 

算法执行时各步的Queue队的值和D数组的值由下表所示:

 

最短路SPFA学习资料(转载)

 

算法执行到第五步后,队Queue空,算法结束。源点V0V1的最短路径为2,到V2的最短路径为5,到V3的最短路径为9,到V4的最短路径为9,结果显然是正确的。

 

SPFA算法模板1:(邻接矩阵)

const int nMax = 1000;           //  顶点的数量上限。
const int eMax = 2000;           //  边的数量上限。
const int inf = 0xffffff;
 
int n, edge[nMax][nMax], dict[nMax];
int queue[eMax];                 //  创建新队列,也可以用STL中的#include<queue>,但效率较低。
bool vis[nMax];
 
void init_data(s){
    for(i = 1; i <= n; i ++)     //  初始化dict[]。
        dict[i] = inf;
    dict[s] = 0;                 //  起点s。
}
 
void spfa(int s){                //  初始结点s,即为起点,若目标结点t,则输出dict[t]。
    init_data(s);
    int head = 0, tail = 1;
    // int path[Max];            //  可增加一个path[]数组来记录最后s到t的路径。
    queue[0] = x;
    dict[s] = 0;
    while(tail > head){
        int u = queue[head];
        vis[u] = true;
        for(i = 1; i <= n; i ++){
            if(dict[i] > dict[u] + edge[u][i]){
                dict[i] = dict[u] + edge[u][i];
                // path[i] = u;
                if(!vis[i]){      //  对以前没有访问过的顶点,加入队列中。
                    vis[i] = true;
                    queue[tail] = i;
                    tail ++;
                }
            }
        }
        vis[u] = false;           //  记得把出队列的顶点的vis[]置为false。
        head ++;
    }
}
 
 
SPFA算法模板2:(邻接表)
 
#include<iostream>
using namespace std;
 
#define MAXE 10000
#define MAXV 1000
int inf = 1000000000;

struct edge_t
{
    int s, t;
    int w;
    int next;
};
edge_t arrEdge[MAXE]; //arrEdge[1...szEdge]保存szEdge条边.
int szEdge;
int arrHead[MAXV];
int nv;
int arrPre[MAXV];
int arrDis[MAXV];
int Que[MAXE*10]; //队列
char inQue[MAXV]; //点v是否在队列中
 
void SPFA(int s)
{
    int i, ie, v, head, tail;
    edge_t e;
    for(i = 1; i <= nv; i++)
        arrDis[i] = inf, arrPre[i] = i;
    arrDis[s] = 0;
    arrPre[s] = -1;
    //初始化队列
    memset(Que, 0, sizeof(Que));
    memset(inQue, 0, sizeof(inQue));
    head = 0, tail = 1;
    Que[head] = s;
    inQue[s] = 1;
    while(head < tail)
    {
        v = Que[head++];
        inQue[v] = 0;
        for(ie = arrHead[v]; ie!=-1; ie = arrEdge[ie].next)
        {
            e = arrEdge[ie];
            if(arrDis[e.t] > arrDis[v] + e.w)
            {
                arrDis[e.t] = arrDis[v] + e.w, arrPre[e.t] = v;
                if(!inQue[e.t])
                {
                    inQue[e.t] = 1;
                    Que[tail++] = e.t;
                }
            }
        }
    }
    return ;
}
 
int main()
{
    int ne, i, s, t, w, p;
    memset(arrHead, -1, sizeof(arrHead));
    memset(arrEdge, 0, sizeof(arrEdge));
    szEdge = 0;
    scanf("%d %d", &nv, &ne);
    for(i = 1; i <= ne; i++)
    {
        scanf("%d %d %d", &s, &t, &w);
        szEdge++;
        arrEdge[szEdge].s = s;
        arrEdge[szEdge].t = t;
        arrEdge[szEdge].w = w;
        arrEdge[szEdge].next = arrHead[s];
        arrHead[s] = szEdge;
        szEdge++;
        arrEdge[szEdge].s = t;
        arrEdge[szEdge].t = s;
        arrEdge[szEdge].w = w;
        arrEdge[szEdge].next = arrHead[t];
        arrHead[t] = szEdge;
    }
    SPFA(1);
    for(i = 1; i <= nv; i++)
    {
        printf("Distance from 1 to %d is %d.\nPath: %d", i, arrDis[i], i);
        p = arrPre[i];
        while(p != -1)
        {
            printf(" <-- %d", p);
            p = arrPre[p];
        }
        printf("\n\n");
    }
    return 0;
}

抱歉!评论已关闭.