SPFA

算法介紹:php

  SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一種隊列實現,減小了沒必要要的冗餘計算。ios

算法流程:算法

   算法大體流程是用一個隊列來進行維護。 初始時將源加入隊列。 每次從隊列中取出一個元素,並對全部與他相鄰的點進行鬆弛,若某個相鄰的點鬆弛成功,則將其入隊。 直到隊列爲空時算法結束。數組

這個算法,簡單的說就是隊列優化的bellman-ford,利用了每一個點不會更新次數太多的特色發明的此算法優化

  SPFA——Shortest Path Faster Algorithm,它能夠在O(kE)的時間複雜度內求出源點到其餘全部點的最短路徑,能夠處理負邊。SPFA的實現甚至比Dijkstra或者Bellman_Ford還要簡單:spa

  設Dist表明S到I點的當前最短距離,Fa表明S到I的當前最短路徑中I點以前的一個點的編號。開始時Dist所有爲+∞,只有Dist[S]=0,Fa所有爲0。code

  維護一個隊列,裏面存放全部須要進行迭代的點。初始時隊列中只有一個點S。用一個布爾數組記錄每一個點是否處在隊列中。blog

  每次迭代,取出隊頭的點v,依次枚舉從v出發的邊v->u,設邊的長度爲len,判斷Dist[v]+len是否小於Dist[u],若小於則改進Dist[u],將Fa[u]記爲v,而且因爲S到u的最短距離變小了,有可能u能夠改進其它的點,因此若u不在隊列中,就將它放入隊尾。這樣一直迭代下去直到隊列變空,也就是S到全部的最短距離都肯定下來,結束算法。若一個點入隊次數超過n,則有負權環。隊列

  SPFA 在形式上和寬度優先搜索很是相似,不一樣的是寬度優先搜索中一個點出了隊列就不可能從新進入隊列,可是SPFA中一個點可能在出隊列以後再次被放入隊列,也就是一個點改進過其它的點以後,過了一段時間可能自己被改進,因而再次用來改進其它的點,這樣反覆迭代下去。設一個點用來做爲迭代點對其它點進行改進的平均次數爲k,有辦法證實對於一般的狀況,k在2左右。 在實際的應用中SPFA的算法時間效率不是很穩定,爲了不最壞狀況的出現,一般使用效率更加穩定的Dijkstra算法。ip

代碼實現:

/*************************************************************************
    > File Name: SPFA.cpp
    > Author: He Xingjie
    > Mail: gxmshxj@163.com
    > Created Time: 2014年06月12日 星期四 18時24分37秒
    > Description: 
 ************************************************************************/
#include<iostream>
#include<queue>
#include<vector>
#include<cstdio>
#include<stack>

using namespace std;

#define MAX 50 
#define INF 65535

typedef struct Edge{
    int w;        //邊的權值
    int end;    //邊的終點
}Edge;

vector<Edge> e[MAX];    //動態數組模擬鄰接鏈表存儲
queue<int> que;        //存儲節點
int dist[MAX], pre[MAX];
bool in[MAX];    //標識是否在隊列裏面
int cnt[MAX];    //記錄入隊次數
int V, E;    //頂點數和邊數

int Init()
{
    int st, end, weight;
    cin>>V>>E;
    for (int i=0; i < E; i++)
    {
        cin>>st>>end>>weight;
        Edge tmp;
        tmp.w = weight;
        tmp.end = end;
        e[st].push_back(tmp);    //存入vector
    }

    for (int i=0; i< V; i++)
    {
        dist[i]= INF;
        cnt[i] = 0;
        in[i] = false;
        pre[i] = 0;
    }
}

bool SPFA(int st)
{
    int i, v, end; 

    dist[st] = 0;
    que.push(st);
    in[st] = true;
    cnt[st]++;
    while (!que.empty())
    {
        v = que.front();
        que.pop();
        in[v] = false;

        for (i=0; i<e[v].size(); i++)
        {
            Edge t = e[v][i];    //取出鄰接邊
            int end = t.end;    //記錄與v鄰接的節點

            if (dist[end] > dist[v] + t.w)    //更新路徑權值,v->end的權值爲w
            {
                dist[end] = dist[v] + t.w;
                pre[end] = v;    //記錄前一個節點
            }
            if (!in[end])    //不在隊列裏面
            {
                que.push(end);
                in[end] = true;
                cnt[end]++;
                if (cnt[end] > V)    //入隊次數大於V次
                {
                    while (!que.empty())    //清空隊列
                    {
                        que.pop();
                    }
                    return false;
                }
            }
        }
    }
    return true;
}

void ShowPath()
{
    int i;
    stack<int> st;

    for (i=0; i<V; i++)
    {
        st.push(i);
        int tmp = pre[i];
        while (tmp != 0)
        {
            st.push(tmp);
            tmp = pre[tmp];
        }

        cout<<"1";
        while (!st.empty())
        {
            tmp = st.top();
            st.pop();
            cout<<"->"<<tmp+1;
        }
        cout<<" : "<<dist[i]<<endl;
    }
}

int main()
{
    bool ret;

    freopen("input.txt", "r", stdin);
    Init();
    ret = SPFA(0);
    
    ShowPath();

    if (ret)
        cout<<"No Negative circle"<<endl;
    else
        cout<<"Exit Negative circle"<<endl;

    return 0;
}

2014/6/13  23:38

輸入數據:

5 9
0 1 3
2 1 4
1 4 7
1 3 1
0 2 8
3 0 2
0 4 -4
4 3 6
3 2 -5

輸出數據:

 

 

參考:

http://www.nocow.cn/index.php/SPFA

相關文章
相關標籤/搜索