bellman-ford演算法是求含負權圖
的單源最短路徑演算法,效率很低,但**很容易寫。即進行不停地鬆弛
(relaxation),每次鬆弛把每條邊都更新一下,若n-1次鬆弛後還能更新,則說明圖中有負環(即負權迴路,本文最後有解釋),無法得出結果,否則就成功完成。bellman-ford演算法有乙個小優化
:每次鬆弛先設乙個旗幟
flag,初值為false,若有邊更新則賦值為true,最終如果還是false則直接成功退出。bellman-ford演算法浪費了許多時間做無必要的鬆弛,所以spfa
演算法用佇列
進行了優化,效果十分顯著,高效難以想象。spfa還有slf
,lll,滾動陣列等優化。
dijkstra演算法
中不允許邊的權是負權,如果遇到負權,則可以採用bellman-ford演算法。
bellman-ford演算法能在更普遍的情況下(存在負權邊)解決單源點最短路徑問題。對於給定的帶權(有向或無向)圖 g=(v,e),其源點為s,加權函式w是 邊集 e 的對映。對圖g執行bellman-ford演算法的結果是乙個布林值,表明圖中是否存在著乙個從源點s可達的負權迴路。若不存在這樣的迴路,演算法將給出從源點s到 圖g的任意頂點v的最短路徑d[v]。
適用條件&範圍
1.單源最短路徑(從源點s到其它所有頂點v);
2.有向圖&無向圖(無向圖可以看作(u,v),(v,u)同屬於邊集e的有向圖);
3.邊權可正可負(如有負權迴路輸出錯誤提示);
4.差分約束系統;
bellman-ford演算法描述:
1,.初始化:將除源點外的所有頂點的最短距離估計值 d[v] ←+∞, d[s] ←0;
2.迭代求解:反覆對邊集e中的每條邊進行鬆弛操作,使得頂點集v中的每個頂點v的最短距離估計值逐步逼近其最短距離;(執行|v|-1次)
3.檢驗負權迴路:判斷邊集e中的每一條邊的兩個端點是否收斂。如果存在未收斂的頂點,則演算法返回false,表明問題無解;否則演算法返回true,並且從源點可達的頂點v的最短距離儲存在 d[v]中。
描述性證明:
首先指出,圖的任意一條最短路徑既不能包含負權迴路,也不會包含正權迴路,因此它最多包含|v|-1條邊。
其次,從源點s可達的所有頂點如果 存在最短路徑,則這些最短路徑構成乙個以s為根的最短路徑樹。bellman-ford演算法的迭代鬆弛操作,實際上就是按頂點距離s的層次,逐層生成這棵最短路徑樹的過程。
在對每條邊進行1遍鬆弛的時候,生成了從s出發,層次至多為1的那些樹枝。也就是說,找到了與s至多有1條邊相聯的那些頂點的最短路徑;對每條邊進行第2遍鬆弛的時候,生成了第2層次的樹枝,就是說找到了經過2條邊相連的那些頂點的最短路徑……。因為最短路徑最多隻包含|v|-1 條邊,所以,只需要迴圈|v|-1 次。
每實施一次鬆弛操作,最短路徑樹上就會有一層頂點達到其最短距離,此後這層頂點的最短距離值就會一直保持不變,不再受後續鬆弛操作的影響。(但是,每次還要判斷鬆弛,這裡浪費了大量的時間,怎麼優化?單純的優化是否可行?)
如果沒有負權迴路,由於最短路徑樹的高度最多只能是|v|-1,所以最多經過|v|-1遍鬆弛操作後,所有從s可達的頂點必將求出最短距離。如果 d[v]仍保持 +∞,則表明從s到v不可達。
如果有負權迴路,那麼第 |v|-1 遍鬆弛操作仍然會成功,這時,負權回路上的頂點不會收斂。
c++ pseudo code
bellman-ford(g,w,s) :
boolean
//圖g
,邊集 函式 w ,
s為源點
for each vertex v ∈ v(
g) do//初始化
1階段
d[ v] ←+∞
d[s] ←
0; //1階段結束
for i=1 to |v|-1
do//2階段開始,雙重迴圈。
for each edge(
u,v) ∈
e(g)
do//邊集陣列要用到,窮舉每條邊。
if d[v]> d[u]+ w(u,v) then //
鬆弛判斷,
w(w,v)是u
到v的權值d[v]=d[u]+w(u,v)
//鬆弛操作
2階段結束
for each edge(
u,v) ∈
e(g)
doif d[v]> d[u]+ w(u,v) then
exit
false
//存在負權迴路
exit true
負權迴路
簡介spfa
求單源最短路的spfa
演算法的全稱是:
shortest path faster algorithm
。spfa演算法是西南交通大學
段凡丁于1994
年發表的
.從名字我們就可以看出,這種演算法在效率上一定有過人之處。
很多時候,給定的圖存在負權邊,這時類似
dijkstra
等演算法便沒有了用武之地,而
bellman-ford演算法
的複雜度又過高,spfa
演算法便派上用場了。
簡潔起見,我們約定有向加權圖g
不存在負權迴路,即
最短路徑
一定存在。當然,我們可以在
執行該演算法前做一次拓撲排序
,以判斷是否存在負權迴路,但這不是我們討論的重點。
我們用陣列d記錄每個結點的
最短路徑
估計值,而且用
鄰接表來儲存圖g
。我們採取的方法是
鬆弛:設立乙個先進先出的
佇列用來儲存待優化的
結點,優化時每次取出隊首結點u
,並且用
u點當前的
最短路徑
估計值對離開u
點所指向的
結點v進行
鬆弛操作
,如果v
點的最短路徑估計值有所調整,且
v點不在當前的
佇列中,就將v
點放入隊尾。這樣不斷從佇列中取出
結點來進行鬆弛操作,直至
佇列空為止。
spfa原理
定理: 只要
最短路徑
存在,上述
spfa
演算法必定能求出最小值。
證明:每次將點放入隊尾,都是經過
鬆弛操作
達到的。換言之,每次的優化將會有某個點v
的最短路徑
估計值d[v]
變小。所以演算法的執行會使
d越來越小。由於我們假定圖中不存在負權迴路,所以每個
結點都有
最短路徑
值。因此,演算法不會無限執行下去,隨著d
值的逐漸變小,直到到達
最短路徑
值時,演算法結束,這時的最短路徑估計值就是對應
結點的最短路徑值。(證畢)
期望的時間
複雜度o(ke), 其中
k為所有頂點進隊的平均次數,可以證明
k一般小於等於2。
實現方法:建立乙個
佇列,初始時
佇列裡只有起始點,再建立乙個
**記錄起始點到所有點的
最短路徑
(該**的初始值要賦為極大值,該點到他本身的路徑賦為0
)。然後執行
鬆弛操作
,用佇列裡有的點去重新整理起始點到所有點的最短路,如果重新整理成功且被重新整理點不在佇列中則把該點加入到佇列最後。重複執行
直到隊列為空
判斷有無負環:
如果某個點進入
佇列的次數超過n
次則存在負環
(存在負環則無
最短路徑
,如果有負環則會無限
鬆弛,而乙個帶n
個點的圖至多鬆弛
n-1次)
對spfa
的乙個很直觀的理解就是由無權圖的bfs
轉化而來
.在無權圖中,
bfs首先到達的頂點所經歷的路徑一定是最短路(也就是經過的最少頂點數)
.所以此時利用
visit[u]
,可以使每個頂點只進隊一次
.在帶權圖中,最先到達的頂點所計算出來的路徑不一定是最短路
.乙個解決方法是放棄
visit
陣列,此時所需時間自然就是指數級的.
所以我們不能放棄
visit
陣列,而是在處理乙個已經在
佇列中且當前所得的路徑比原來更好的頂點時,直接更新最優解.
Bellman Ford演算法和SPFA演算法
最短路徑是圖論中乙個很經典的問題 給定圖g v,e 求一條從起點到終點的路徑,使得這條路徑上經過的所有邊的邊權之和最小。對任意給出的圖g v,e 和起點s 終點t,如何求從s到t的最短路徑。解決最短路徑問題的常用演算法有dijkstra演算法 bellman ford演算法 spea演算法和floy...
Bellman Ford演算法和SPFA演算法
演算法介紹 dijkstra可以解決單源無負邊最短路徑問題。但是當遇到含有負邊的單源最短路徑問題就需要使用bellman ford演算法來解決。bellman ford演算法還可以檢測出負環。演算法步驟 我們可以看到bellman ford演算法的演算法結構是比較簡單的,複雜度是o v e o ve...
Bellman Ford演算法和Dijkstra演算法
bellman ford演算法是通過relax邊來實現的,由於最短無負權迴路的路徑應該最多有v 1條邊,所以一共執行v 1次relax操作即可,而且注意,每次relax操作都只是基於上一次relax操作之後的圖,和這次relax中已經relax了的節點毫無關係 這個是重點!檢查負權迴路原理 若有負權...