基本概念算法
圖:ide
有頂點和邊組成。又分爲3d
有向圖:code
在這裏只能從A到B,不能從B到A。blog
無向圖:隊列
能從A到B,也能從B到A,也能夠用下圖表示:it
還有就是給邊加上權重,變成加權圖:class
權重表明了兩個頂點鏈接的程度,它能夠是時間、距離、路費等等,根據實際狀況而定。搜索
最短路徑:循環
如上圖,從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,其餘節點皆爲∞。
一、處理AB,B的權重改成1。此時A=0,B=1,其他爲∞。B的起點爲A。
二、處理AC,C的權重改成7。此時A=0,B=1,C=7。C的起點爲A。
三、處理AD,D的權重改成6。此時A=0,B=1,C=7,D=6。D的起點爲A。
四、處理BA,BA=1+1=2>0,無須進行鬆弛操做。
五、處理BC,BC=1+1=2<7,C的權重改成2,此時A=0,B=1,C=2,D=6。C的起點改成B。
六、接着繼續處理,本次對全部邊的循環,得出以下結果:
A到各點最低消耗:A=0,B=1,C=2,D=6,E=4
各點的起點:B<--A,C<--B,D<--A,E<--C。
七、接着開啓下一輪對全部邊的循環,在這次循環中,沒有進行鬆弛操做,故跳出循環。
八、判斷沒有負環路,至此得出最終結果。