單源最短路徑(3) SPFA 演算法

2022-01-10 12:43:46 字數 2924 閱讀 8860

spfa(shortest path faster algorithm)演算法,是西南交通大學段凡丁于 1994 年發表的,其在 bellman-ford 演算法的基礎上加上乙個佇列優化,減少了冗餘的鬆弛操作,是一種高效的最短路演算法。

設立乙個佇列用來儲存待優化的頂點,優化時每次取出隊首頂點 u,並且用 u 點當前的最短路徑估計值dist[u]對與 u 點鄰接的頂點 v 進行鬆弛操作,如果 v 點的最短路徑估計值dist[v]可以更小,且 v 點不在當前的佇列中,就將 v 點放入隊尾。這樣不斷從佇列中取出頂點來進行鬆弛操作,直至佇列空為止。(所謂的鬆弛操作,簡單來說,對於頂點 i,把dist[i]調整更小。更多解釋請參考百科:鬆弛操作)

而其檢測負權迴路的方法也很簡單,如果某個點進入佇列的次數大於等於 n,則存在負權迴路,其中 n 為圖的頂點數。

#include #include #include 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()

queueq;

q.push(source);

dist[source] = 0;

visited[source] = 1;

enqueue_num[source]++;

while (!q.empty())}}

}}

return true;

}void print()

cout << "--" << source << endl;}}

}int main()

if (spfa())

print();

else

cout << "存在負權迴路!\n";

return 0;

}

執行如下:

/* test 1 */

請輸入圖的頂點數,邊數,源點:5 7 0

請輸入 7 條邊的資訊:

0 1 100

0 2 30

0 4 10

2 1 60

2 3 60

3 1 10

4 3 50

0 到 1 的最短距離是:70,路徑是:1--3--4--0

0 到 2 的最短距離是:30,路徑是:2--0

0 到 3 的最短距離是:60,路徑是:3--4--0

0 到 4 的最短距離是:10,路徑是:4--0

/* test 2 */

請輸入圖的頂點數,邊數,源點:4 6 0

請輸入 6 條邊的資訊:

0 1 20

0 2 5

3 0 -200

1 3 4

3 1 4

2 3 2

存在負權迴路!

如果某個點進入佇列的次數大於等於 n,則存在負權迴路。為什麼偏偏是 n?

對於乙個不存在負權迴路的圖,設其頂點數為 n,我們把圖稍微「轉換」下,如下圖 a:

其中 k≤n-1,當 k=n-1 時,即為上圖 b。

每操作完乙個批次的點,至少有乙個點的最短路徑被確定。這裡讀者只需從 dijkstra 演算法方面來考慮即可。dijkstra 每次迴圈都找出dist裡的最小值,可以對應到這裡的每個批次。

乙個不存在負權迴路的圖,最多有 n-1 個批次,每做完乙個批次至少有乙個點的最短路徑被確定,即乙個點的入隊次數不超過 n-1。因為若乙個頂點要入佇列,則必存在一條權值之和更小的路徑,而在最多做完 n-1 個批次後,所有頂點的最短路徑都被確定。(這裡需要注意的是,如果乙個批次中,有多條路徑對某頂點進行更新,則該頂點只會被入隊一次,這從**就可以看出)

對於乙個不存在負權迴路的圖,我們假設其頂點數為 n,邊數為 m。

引自 spfa **:考慮乙個隨機圖,運用均攤分析的思想,每個點的平均出度為 \(o(\frac m n)\),而每個點的平均入隊次數為 2,因此時間複雜度為 \(o(n⋅\frac m n⋅2)=o(2m)=o(m)\)。

關於上述的「平均入隊次數為 2」,2 這個數字從何得來,我也找不到證明,從網上各位朋友對此的一致態度:尚待商榷。但是可以確定的是,spfa 演算法在隨機圖中的平均效能是優於 bellman_ford 演算法的。

spfa 的最佳時間複雜度為 \(o(n)\)。比如上圖 b,每個點只入隊一次。

接著再看下 spfa 的最差時間複雜度,它發生在乙個完全圖中,如下圖(為突出重點,其餘邊未畫出),

我們約定,0 點為源點,每次更新完 k 點出隊後,k+1​ 點都可以再次對 k 點進行更新併入隊,其中 ​1≤ k≤ n-2​。那麼我們得出:

0 點,入隊 1 次;

1 點,入隊 n-1 次;

2 點,入隊 n-2 次;

3 點,入隊 n-3 次;

.n-2 點,入隊 2 次;

n-1 點,入隊 1 次;

因為是完全圖,所以每個點的出度為 n-1,因此總的時間複雜度為:

\[(n-1)⋅[1+1+2+3+...+(n-2)+(n-1)]=o(n^3)

\]由於是完全圖,也可以表達成 \(o(nm)\)。很容易看出,spfa 演算法的時間複雜度很不穩定。

單源最短路徑(3) SPFA 演算法

spfa shortest path faster algorithm 演算法,是西南交通大學段凡丁于 1994 年發表的,其在 bellman ford 演算法的基礎上加上乙個佇列優化,減少了冗餘的鬆弛操作,是一種高效的最短路演算法。設立乙個佇列用來儲存待優化的頂點,優化時每次取出隊首頂點 u,並...

單源最短路徑 SPFA演算法

求單源最短路徑的spfa演算法在bellman ford演算法的基礎上進行了改進,使其在能夠計算帶負權圖的單源最短路徑的基礎上,時間複雜度大幅度降低。時間複雜度 o k e k 2 基本演算法 設立乙個先進先出的佇列來儲存待優化的節點,優化時每次取出隊首節點u,並且用u點當前的最短路徑估計值對離開u...

Spfa單源最短路徑演算法

spfa 全稱shortest path faster algorithm,是求單源最短路徑的一種演算法.跟dijkstra類似,但是有一些不一樣。由於本人弱,不知道 有區別,請各位大神補充 我們來建乙個圖。首先,我們用e陣列表示各邊的關係,如下是初始狀態。我們開乙個dis陣列來儲存1號點到各點的最...