Bellman-Ford 算法 & SPFA(單源最短路問題)

一、Bellman-Ford算法

Bellman-Ford 算法是單源最短路問題的一種算法,相比 Dijkstra 算法,它可以處理含有負權迴路(也叫負權環,negative cycles)的圖。

Dijkstra 算法以貪心法選取未被處理的具有最小權值的節點,然後對其的出邊進行鬆弛操作;
而 Bellman-Ford 算法簡單地對所有邊進行鬆弛操作,共|V| − 1次,其中 |V|是圖的點的數量。 —— [ 維基百科 ]

至於 Bellman-Ford 算法如何判定負權迴路,先要證明不存在負權迴路的圖的最短路徑最長不會經過超過V-1條邊(也可以說是沒有環,這種無環路徑被稱爲簡單路徑)。
因爲在圖中,環有零環(環上邊的權重和 = 0,正環和負環類似)、正環和負環三種。如果是正環,繞一圈後路徑長度反而變長,所以對最短路徑沒有影響;繞零環一圈也沒有影響;但如果遇到負環,最短路徑可以不停的繞着這個環使得路徑長度越來越短,也就是沒有最短路徑(最短路徑長度爲-∞)。
也就是說,正常情況下每條邊最多被鬆弛V-1次(請自己思考爲什麼),只有負權迴路上的邊纔可以無限地鬆弛。Bellman-Ford 算法判定的就是是否有邊在第V-1次鬆弛後還能繼續鬆弛。

//Bellman-Ford 算法
//distance[i]: 從源點(編號爲0)到節點i的路徑的距離
//weight[i]: 第i條邊的權重

// 步驟1:初始化圖
for (int i=0; i<N; i++)
    distance[v] = INF;
distance[0] = 0;

// 步驟2:重複對每一條邊進行鬆弛操作
for (int k=0; k<V-1; k++)
    for (int i=0; i<E; i++)
        if (distance[u[i]] + weight[i] < distance[v[i]])
            distance[v[i]] = distance[u[i]] + weight[i]; //鬆弛

// 步驟3:檢查負權環
for (int i=0; i<E; i++)
    if (distance[u[i]] + weight[i] < distance[v[i]])
        cout<<"圖包含了負權環"<<endl;

當然,在步驟2中,不一定要找V-1次。在實際應用中,經常會在未達到V-1次前就出解,V-1其實是最大值。於是可以在循環中設置判定,在某次循環不再進行鬆弛時,直接退出循環,進行負權環判定。

// 步驟2:重複對每一條邊進行鬆弛操作
bool flag; //判定標誌
for (int k=0; k<V-1; k++)
{
    flag = true;
    for (int i=0; i<E; i++)
        if (distance[u[i]] + weight[i] < distance[v[i]])
        {
            distance[v[i]] = distance[u[i]] + weight[i]; //鬆弛
            flag = false;
        }
    if (flag) break; //這輪循環不再進行鬆弛
}

很明顯,Bellman-Ford 算法的時間複雜度是O(|V||E|)。

二、SPFA

後來,出現了 SPFA (Shortest Path Faster Algorithm),但一般國際上不承認,因爲它實際上就是 Bellman-Ford 算法的隊列實現。
相對 Bellman-Ford 算法,它減少了不必要的冗餘計算。 算法大致流程是用一個隊列來進行維護。 初始時將源加入隊列。 每次從隊列中取出一個元素, 並對所有它的鄰接點進行鬆弛,若某個鄰接點鬆弛成功,則將其入隊,直到隊列爲空。
不過奇怪的是,SPFA無法處理存在負權迴路的圖。
(程序略)
SPFA的最壞時間複雜度也是O(|V||E|),與 Bellman-Ford 算法相同,但對於隨機數據,SPFA往往只需要很短的時間就能求出最短路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章