最短路徑——SPFA算法

1、前提引入ios

咱們學過了Bellman-Ford算法,如今又要提出這個SPFA算法,爲何呢?算法

考慮一個隨機圖(點和邊隨機生成),除了已肯定最短路的頂點與還沒有肯定最短路的頂點之間的邊,其它的邊所作的都是無用的,大體描述爲下圖(分割線以左爲已肯定最短路的頂點):數組

其中紅色部分爲所作無用的邊,藍色部分爲實際有用的邊。既然只需用到中間藍色部分的邊,那就是SPFA算法的優點之處了。優化

 

2、算法描述spa

算法特色:在 Bellman-ford 算法的基礎上加上一個隊列優化,減小了冗餘的鬆弛操做,是一種高效的最短路算法。code

時間複雜度:O(mn)blog

關鍵詞:初始化    鬆弛操做    隊列隊列

主要變量以下:ci

int n              表示有n個點,從1~n標號it

int s,t            s爲源點,t爲終點

int dis[N]           dis[i]表示源點s到點i的最短路徑

int pre[N]          記錄路徑,pre[i]表示i的前驅結點

bool vis[N]          vis[i]=true表示點i在隊列中

queue<int> q     隊列,在整個算法中有頂點入隊了要記得標記vis數組,有頂點出隊了記得消除那個標記

【初始化】

dis數組所有賦值爲INF,pre數組所有賦值爲-1(表示還不知道前驅),

dis[s] = 0 表示源點不要求最短路徑(或者最短路徑就是0)。

【隊列+鬆弛操做】

讀取隊頭頂點u,並將隊頭頂點u出隊(記得消除標記);將與點u相連全部點v進行鬆弛操做,若是能更新估計值(即令d[v]變小),那麼就更新,另外,若是點v沒有在隊列中,那麼要將點v入隊(記得標記),若是已經在隊列中了,那麼就不用入隊,這樣不斷從隊列中取出頂點來進行鬆弛操做。

以此循環,直到隊空爲止就完成了單源最短路的求解。

【算法過程】

設立一個隊列用來保存待優化的頂點,優化時每次取出隊首頂點 u,而且用 u 點當前的最短路徑估計值dis[u]對與 u 點鄰接的頂點 v 進行鬆弛操做,若是 v 點的最短路徑估計值dis[v]能夠更小, v 點不在當前的隊列中,就將 v 點放入隊尾。這樣不斷從隊列中取出頂點來進行鬆弛操做,直至隊列空爲止。

【檢測負權迴路】

方法:若是某個點進入隊列的次數大於等於 n,則存在負權迴路,其中 n 爲圖的頂點數。

說明:SPFA沒法處理帶負環的圖。

 

3、代碼實現

#include<iostream>    
#include<queue>
#include<stack>
using namespace std;

int matrix[100][100];  //鄰接矩陣
bool visited[100];     //標記數組
int dist[100];         //源點到頂點i的最短距離
int path[100];         //記錄最短路的路徑
int enqueue_num[100];  //記錄入隊次數
int vertex_num;        //頂點數
int edge_num;          //邊數
int source;            //源點

bool SPFA()
{
    memset(visited, 0, sizeof(visited));
    memset(enqueue_num, 0, sizeof(enqueue_num));
    for (int i = 0; i < vertex_num; i++)
    {
        dist[i] = INT_MAX;
        path[i] = source;
    }

    queue<int> Q;
    Q.push(source);
    dist[source] = 0;
    visited[source] = 1;
    enqueue_num[source]++;
    while (!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        visited[u] = 0;
        for (int v = 0; v < vertex_num; v++)
        {
            if (matrix[u][v] != INT_MAX)  //u與v直接鄰接
            {
                if (dist[u] + matrix[u][v] < dist[v])
                {
                    dist[v] = dist[u] + matrix[u][v];
                    path[v] = u;
                    if (!visited[v])
                    {
                        Q.push(v);
                        enqueue_num[v]++;
                        if (enqueue_num[v] >= vertex_num)
                            return false;
                        visited[v] = 1;
                    }
                }
            }
        }
    }
    return true;
}

void Print()
{
    for (int i = 0; i < vertex_num; i++)
    {
        if (i != source)
        {
            int p = i;
            stack<int> s;
            cout << "頂點 " << source << " 到頂點 " << p << " 的最短路徑是: ";

            while (source != p)  //路徑順序是逆向的,因此先保存到棧
            {
                s.push(p);
                p = path[p];
            }

            cout << source;
            while (!s.empty())  //依次從棧中取出的纔是正序路徑
            {
                cout << "--" << s.top();
                s.pop();
            }
            cout << "    最短路徑長度是:" << dist[i] << endl;
        }
    }
}

int main()
{

    cout << "請輸入圖的頂點數,邊數,源點:";
    cin >> vertex_num >> edge_num >> source;

    for (int i = 0; i < vertex_num; i++)
        for (int j = 0; j < vertex_num; j++)
            matrix[i][j] = INT_MAX;  //初始化matrix數組

    cout << "請輸入" << edge_num << "條邊的信息:\n";
    int u, v, w;
    for (int i = 0; i < edge_num; i++)
    {
        cin >> u >> v >> w;
        matrix[u][v] = w;
    }

    if (SPFA())
        Print();
    else
        cout << "Sorry,it have negative circle!\n";

    return 0;
}

 運行以下:

相關文章
相關標籤/搜索