一周一演算法 演算法7 Dijkstra最短路演算法

2022-03-13 09:22:35 字數 2815 閱讀 3974

上週我們介紹了神奇的只有五行的floyd最短路演算法,它可以方便的求得任意兩點的最短路徑,這稱為「多源最短路」。本週來來介紹指定乙個點(源點)到其餘各個頂點的最短路徑,也叫做「單源最短路徑」。例如求下圖中的1號頂點到2、3、4、5、6號頂點的最短路徑。

與floyd-warshall演算法一樣這裡仍然使用二維陣列e來儲存頂點之間邊的關係,初始值如下。

我們還需要用乙個一維陣列dis來儲存1號頂點到其餘各個頂點的初始路程,如下。

我們將此時dis陣列中的值稱為最短路的「估計值」。

既然是求1號頂點到其餘各個頂點的最短路程,那就先找乙個離1號頂點最近的頂點。通過陣列dis可知當前離1號頂點最近是2號頂點。當選擇了2號頂點後,dis[2]的值就已經從「估計值」變為了「確定值」,即1號頂點到2號頂點的最短路程就是當前dis[2]值。為什麼呢?你想啊,目前離1號頂點最近的是2號頂點,並且這個圖所有的邊都是正數,那麼肯定不可能通過第三個頂點中轉,使得1號頂點到2號頂點的路程進一步縮短了。因為1號頂點到其它頂點的路程肯定沒有1號到2號頂點短,對吧o(∩_∩)o~

既然選了2號頂點,接下來再來看2號頂點有哪些出邊呢。有2->3和2->4這兩條邊。先討論通過2->3這條邊能否讓1號頂點到3號頂點的路程變短。也就是說現在來比較dis[3]和dis[2]+e[2][3]的大小。其中dis[3]表示1號頂點到3號頂點的路程。dis[2]+e[2][3]中dis[2]表示1號頂點到2號頂點的路程,e[2][3]表示2->3這條邊。所以dis[2]+e[2][3]就表示從1號頂點先到2號頂點,再通過2->3這條邊,到達3號頂點的路程。

我們發現dis[3]=12,dis[2]+e[2][3]=1+9=10,dis[3]>dis[2]+e[2][3],因此dis[3]要更新為10。這個過程有個專業術語叫做「鬆弛」。即1號頂點到3號頂點的路程即dis[3],通過2->3這條邊鬆弛成功。這便是dijkstra演算法的主要思想:通過「邊」來鬆弛1號頂點到其餘各個頂點的路程。

同理通過2->4(e[2][4]),可以將dis[4]的值從∞鬆弛為4(dis[4]初始為∞,dis[2]+e[2][4]=1+3=4,dis[4]>dis[2]+e[2][4],因此dis[4]要更新為4)。

剛才我們對2號頂點所有的出邊進行了鬆弛。鬆弛完畢之後dis陣列為:

接下來,繼續在剩下的3、4、5和6號頂點中,選出離1號頂點最近的頂點。通過上面更新過dis陣列,當前離1號頂點最近是4號頂點。此時,dis[4]的值已經從「估計值」變為了「確定值」。下面繼續對4號頂點的所有出邊(4->3,4->5和4->6)用剛才的方法進行鬆弛。鬆弛完畢之後dis陣列為:

繼續在剩下的3、5和6號頂點中,選出離1號頂點最近的頂點,這次選擇3號頂點。此時,dis[3]的值已經從「估計值」變為了「確定值」。對3號頂點的所有出邊(3->5)進行鬆弛。鬆弛完畢之後dis陣列為:

繼續在剩下的5和6號頂點中,選出離1號頂點最近的頂點,這次選擇5號頂點。此時,dis[5]的值已經從「估計值」變為了「確定值」。對5號頂點的所有出邊(5->4)進行鬆弛。鬆弛完畢之後dis陣列為:

最後對6號頂點所有點出邊進行鬆弛。因為這個例子中6號頂點沒有出邊,因此不用處理。到此,dis陣列中所有的值都已經從「估計值」變為了「確定值」。

最終dis陣列如下,這便是1號頂點到其餘各個頂點的最短路徑。

ok,現在來總結一下剛才的演算法。演算法的基本思想是:每次找到離源點(上面例子的源點就是1號頂點)最近的乙個頂點,然後以該頂點為中心進行擴充套件,最終得到源點到其餘所有點的最短路徑。基本步驟如下:

完整的dijkstra演算法**如下:

#include int

main()

//初始化dis陣列,這裡是1號頂點到其餘各個頂點的初始路程

for(i=1;i<=n;i++)

dis[i]=e[1

][i];

//book陣列初始化

for(i=1;i<=n;i++)

book[i]=0

; book[

1]=1

;

//dijkstra演算法核心語句

for(i=1;i<=n-1;i++)

}book[u]=1

;

for(v=1;v<=n;v++)

}

}//輸出最終的結果

for(i=1;i<=n;i++)

printf(

"%d

",dis[i]);

getchar();

getchar();

return0;

}

可以輸入以下資料進行驗證。第一行兩個整數n  m。n表示頂點個數(頂點編號為1~n),m表示邊的條數。接下來m行表示,每行有3個數x y z。表示頂點x到頂點y邊的權值為z。

691

2113

12239

2433

5543

44513

46155

64

執行結果是

018

41317

通過上面的**我們可以看出,這個演算法的時間複雜度是o(n*2*n)即o(n2)。其中每次找到離1號頂點最近的頂點的時間複雜度是o(n),這裡我們可以用「堆」(將再下一章學到)來優化,使得這一部分的時間複雜度降低到o(logn)。另外對於邊數m少於n2

的稀疏圖來說(我們把m遠小於n2

的圖稱為稀疏圖,而m相對較大的圖稱為稠密圖),我們可以用鄰接表(這是個神馬東西?不要著急,下週再仔細講解)來代替鄰接矩陣,使得整個時間複雜度優化到o(mlogn)。請注意!在最壞的情況下m就是n2

,這樣的話mlogn要比n2

還要大。但是大多數情況下並不會有那麼多邊,因此mlogn要比n2

小很多。

【一周一演算法】演算法7:dijkstra最短路演算法

一周一演算法 小哼買書

再來看乙個具體的例子 小哼買書 來看看三個排序在應用上的區別和侷限性。小哼的學校要建立乙個圖書角,老師派小哼去找一些同學做調查,看看同學們都喜歡讀哪些書。小哼讓每個同學寫出乙個自己最想讀的書的isbn號 你知道嗎?每本書都有唯一的isbn號,不信話你去找本書翻到背面看看 當然有一些好書會有很多同學都...

每週一演算法 快速排序

public class quicksort 快速排序 public void recquicksort int left,int right public int partitionit int left,int right,long pivot else return leftptr publi...

一周一道演算法題(1)

本文旨在想提高演算法的同學,時間間隔一周,同學們完全可以用碎片化的時間來逐步提公升自己 給定乙個整數陣列nums和乙個目標值target,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們的陣列下標。你可以假設每種輸入只會對應乙個答案。但是,你不能重複利用這個陣列中同樣的元素。給定 nums ...