最短路徑之貝爾曼-福特算法

基本概念

圖:

最短路徑之貝爾曼-福特算法

有頂點和邊組成。又分爲

有向圖:

最短路徑之貝爾曼-福特算法

在這裏只能從A到B,不能從B到A。

無向圖:

最短路徑之貝爾曼-福特算法

能從A到B,也能從B到A,也可以用下圖表示:

最短路徑之貝爾曼-福特算法

還有就是給邊加上權重,變成加權圖:

最短路徑之貝爾曼-福特算法

權重代表了兩個頂點連接的程度,它可以是時間、距離、路費等等,根據實際情況而定。

最短路徑:

最短路徑之貝爾曼-福特算法

如上圖,從A到D,有三種路徑:ABD、AD、ACD。

考慮到邊的權重(比如路費),三條線路中最短路徑不是兩點直連的AD(10),而是ABD(2+3=5)。

負環路:

雖然從現實場景中,人們很難想象邊的權重是負數——沒聽說過走高速從A到B,不用交高速費,還倒找錢的。

但是從理論上來說,還是要考慮負權邊的存在,這就導致一個問題:負環路的存在導致無法找出最短路徑。

看下圖:

最短路徑之貝爾曼-福特算法

從A到A,花費的是0,這是最短路徑了,但是因爲有了負權邊的存在,會造成:

A-B-C-A,權重爲2+2-5=-1,也就是說A繞了一圈,變成-1,比0小,最短路徑是ABCA了。

這還沒完,再繞一圈,-1+2+2-5=-2,A變成了-2,照此循環下去,A到A的權重會越來越小。

這就是負環路,永遠找不到最短路徑。

當然,有負權邊不代表一定有負環路,如下圖:

最短路徑之貝爾曼-福特算法

這就沒有形成負環路,A到C的最短路徑就是ABC=3

廣度搜索優先:

簡單地說,就是從根節點開始,搜索完其子節點後,再搜索子節點的子節點,直至找到目標節點或所有節點都被遍歷一遍。

最短路徑之貝爾曼-福特算法

如上圖,將根節點A的子節點BCD放入隊列,取出B,再將B的子節點EF放入隊列,接着取出C,再將C的子節點G放入隊列,按照隊列先進先出的特性,遍歷所有節點。

深度搜索優先:

從根節點開始,搜索完一條分支後,再搜索另一分支。

最短路徑之貝爾曼-福特算法

如上圖,取根節點A壓入棧。

取出A,獲取A的子節點BCD,壓入棧。

取出B,獲取B的子節點EF,壓入棧。

取出F,並無子節點,且不是目標節點,拋出。E同理。

取出C,獲取C的子節點G,壓入棧。

取出G,同F。

取出D,獲取D的子節點H,壓入棧。

取出H,同F。

鬆弛操作:

最短路徑之貝爾曼-福特算法

如上圖,算出A到D的最短路徑。

一開始我們只知道A到A的路徑是0,到BCD的路徑未知,就設爲∞。

計算A-D路徑爲0+10=10 <∞,故將D由∞改爲10。這就是一次鬆弛操作。

貝爾曼-福特算法

簡單地說,就是對圖中所有訂單、所有邊都進行鬆弛操作,直到找到最短路徑。所以其時間複雜度應該是O(頂點數*邊數)。

僞代碼應該是:


for(int i=0;i<頂點數-1;i++){
 for(int j=0;j<邊數;j++){
  鬆弛操作;
 }
}

但在實際情況中,在小於“頂點數-1”次的遍歷中,已經求出了最短路徑,所以在內部循環結束後,校驗一下有沒有進行鬆弛操作,如果沒有,則說明已求出最短路徑,直接跳出即可。

僞代碼:

for(int i=0;i<頂點數-1;i++){
 是否進行了鬆弛操作=false;
 for(int j=0;j<邊數;j++){
  if(終點權重>起點權重+邊權重){
   鬆弛操作:終點權重=起點權重+邊權重;終點的起點設爲起點名稱;
   是否進行了鬆弛操作=true;
  }
 }
 if(沒有進行鬆弛操作){
  break;
 }
}

再結合之前談到的負環路,在執行完最多頂點數-1次循環後,理應得到最短路徑,如果我們額外再遍歷一次所有的邊,看看有沒有進行鬆弛操作。如果有,說明存在負環路。

添加僞代碼:


是否存在負環路=false;
for(int j=0;j<邊數;j++){
 if(終點權重>起點權重+邊權重){
  是否存在負環路=true;
  break;  
 }
}

操作步驟:

最短路徑之貝爾曼-福特算法

如上圖,共有5個頂點:ABCDE。

16條邊(無向圖,每條線代表兩個邊):AB、AC、AD、BA、BC、BE、CA、CB、CD、CE、DA、DC、DE、EB、EC、ED

以A爲起點,計算到其他頂點的最短路徑。

初始狀態下,A的權重應爲0,其他節點皆爲∞。

1、處理AB,B的權重改爲1。此時A=0,B=1,其餘爲∞。B的起點爲A。

2、處理AC,C的權重改爲7。此時A=0,B=1,C=7。C的起點爲A。

3、處理AD,D的權重改爲6。此時A=0,B=1,C=7,D=6。D的起點爲A。

4、處理BA,BA=1+1=2>0,無須進行鬆弛操作。

5、處理BC,BC=1+1=2<7,C的權重改爲2,此時A=0,B=1,C=2,D=6。C的起點改爲B。

6、接着繼續處理,本次對所有邊的循環,得出如下結果:

A到各點最低消耗:A=0,B=1,C=2,D=6,E=4

各點的起點:B<--A,C<--B,D<--A,E<--C。

7、接着開啓下一輪對所有邊的循環,在此次循環中,沒有進行鬆弛操作,故跳出循環。

8、判斷沒有負環路,至此得出最終結果。

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