當前位置:首頁 » 操作系統 » dijkstra演算法最短路徑

dijkstra演算法最短路徑

發布時間: 2023-02-18 18:28:21

1. dijkstra演算法是什麼

Dijkstra演算法是由荷蘭計算機科學家狄克斯特拉(Dijkstra)於1959年提出的,因此又叫狄克斯特拉演算法。是從一個頂點到其餘各頂點的最短路徑演算法,解決的是有向圖中最短路徑問題。

其基本原理是:每次新擴展一個距離最短的點,更新與其相鄰的點的距離。當所有邊權都為正時,由於不會存在一個距離更短的沒擴展過的點,所以這個點的距離永遠不會再被改變,因而保證了演算法的正確性。

不過根據這個原理,用Dijkstra求最短路的圖不能有負權邊,因為擴展到負權邊的時候會產生更短的距離,有可能就破壞了已經更新的點距離不會改變的性質。

舉例來說,如果圖中的頂點表示城市,而邊上的權重表示著城市間開車行經的距離。Dijkstra演算法可以用來找到兩個城市之間的最短路徑。

Dijkstra演算法的輸入包含了一個有權重的有向圖G,以及G中的一個來源頂點S。我們以V表示G中所有頂點的集合。每一個圖中的邊,都是兩個頂點所形成的有序元素對。(u,v)表示從頂點u到v有路徑相連。我們以E所有邊的集合,而邊的權重則由權重函數w: E→[0,∞]定義。

因此,w(u,v)就是從頂點u到頂點v的非負花費值(cost)。邊的花費可以想像成兩個頂點之間的距離。任兩點間路徑的花費值,就是該路徑上所有邊的花費值總和。

已知有V中有頂點s及t,Dijkstra演算法可以找到s到t的最低花費路徑(i.e.最短路徑)。這個演算法也可以在一個圖中,找到從一個頂點s到任何其他頂點的最短路徑。

2. 圖遍歷演算法之最短路徑Dijkstra演算法

最短路徑問題是圖論研究中一個經典演算法問題,旨在尋找圖中兩節點或單個節點到其他節點之間的最短路徑。根據問題的不同,演算法的具體形式包括:

常用的最短路徑演算法包括:Dijkstra演算法,A 演算法,Bellman-Ford演算法,SPFA演算法(Bellman-Ford演算法的改進版本),Floyd-Warshall演算法,Johnson演算法以及Bi-direction BFS演算法。本文將重點介紹Dijkstra演算法的原理以及實現。

Dijkstra演算法,翻譯作戴克斯特拉演算法或迪傑斯特拉演算法,於1956年由荷蘭計算機科學家艾茲赫爾.戴克斯特拉提出,用於解決賦權有向圖的 單源最短路徑問題 。所謂單源最短路徑問題是指確定起點,尋找該節點到圖中任意節點的最短路徑,演算法可用於尋找兩個城市中的最短路徑或是解決著名的旅行商問題。

問題描述 :在無向圖 中, 為圖節點的集合, 為節點之間連線邊的集合。假設每條邊 的權重為 ,找到由頂點 到其餘各個節點的最短路徑(單源最短路徑)。

為帶權無向圖,圖中頂點 分為兩組,第一組為已求出最短路徑的頂點集合(用 表示)。初始時 只有源點,當求得一條最短路徑時,便將新增頂點添加進 ,直到所有頂點加入 中,演算法結束。第二組為未確定最短路徑頂點集合(用 表示),隨著 中頂點增加, 中頂點逐漸減少。

以下圖為例,對Dijkstra演算法的工作流程進行演示(以頂點 為起點):

註:
01) 是已計算出最短路徑的頂點集合;
02) 是未計算出最短路徑的頂點集合;
03) 表示頂點 到頂點 的最短距離為3
第1步 :選取頂點 添加進


第2步 :選取頂點 添加進 ,更新 中頂點最短距離




第3步 :選取頂點 添加進 ,更新 中頂點最短距離




第4步 :選取頂點 添加進 ,更新 中頂點最短距離





第5步 :選取頂點 添加進 ,更新 中頂點最短距離



第6步 :選取頂點 添加進 ,更新 中頂點最短距離



第7步 :選取頂點 添加進 ,更新 中頂點最短距離

示例:node編號1-7分別代表A,B,C,D,E,F,G

(s.paths <- shortest.paths(g, algorithm = "dijkstra"))輸出結果:

(s.paths <- shortest.paths(g,4, algorithm = "dijkstra"))輸出結果:

示例:

找到D(4)到G(7)的最短路徑:

[1] 維基網路,最短路徑問題: https://zh.wikipedia.org/wiki/%E6%9C%80%E7%9F%AD%E8%B7%AF%E9%97%AE%E9%A2%98 ;
[2]CSDN,Dijkstra演算法原理: https://blog.csdn.net/yalishadaa/article/details/55827681 ;
[3]RDocumentation: https://www.rdocumentation.org/packages/RNeo4j/versions/1.6.4/topics/dijkstra ;
[4]RDocumentation: https://www.rdocumentation.org/packages/igraph/versions/0.1.1/topics/shortest.paths ;
[5]Pypi: https://pypi.org/project/Dijkstar/

3. 最短路徑演算法(Dijkstra)

Dijkstra( 迪科斯特拉 )演算法是用來解決單源最短路徑的演算法,要求路徑權值非負數。該演算法利用了深度優先搜索和貪心的演算法。

下面是一個有權圖,求從A到各個節點的最短路徑。

第1步:從A點出發,判斷每個點到A點的路徑(如果該點不能直連A點則距離值為無窮大,如果該點能和A直連則是當前的權值),計算完之後把A點上色,結果如下圖:

第2步:從除A點之外的點查找到距離A點最近的點C,從C點出發查找其鄰近的節點(除去已上色的點),並重新計算C點的鄰近點距離A點的值,如圖中B點,若新值(C點到A點的值+C點到該點的路徑)小於原值,則將值更新為5,同理更新D、E點。同時將C標記為已經處理過,如圖所示塗色。

第3步:從上色的節點中查找距離A最近的B點,重復第3步操作。

第4步: 重復第3步,2步,直到所有的節點都上色。

最後就算出了從A點到所有點的最短距離。

leetcode 743題

4. 最短路徑 | 深入淺出Dijkstra演算法(一)

上次我們介紹了神奇的只有 五行的 Floyd-Warshall 最短路演算法 ,它可以方便的求得 任意兩點的最短路徑, 這稱為 「多源最短路」。

這次來介紹 指定一個點(源點)到其餘各個頂點的最短路徑, 也叫做 「單源最短路徑」。 例如求下圖中的 1 號頂點到 2、3、4、5、6 號頂點的最短路徑。

與 Floyd-Warshall 演算法一樣,這里仍然 使用二維數組 e 來存儲頂點之間邊的關系, 初始值如下。

我們還需要用 一個一維數組 dis 來存儲 1 號頂點到其餘各個頂點的初始路程, 我們可以稱 dis 數組為 「距離表」, 如下。

我們將此時 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,現在來總結一下剛才的演算法。 Dijkstra演算法的基本思想是:每次找到離源點(上面例子的源點就是 1 號頂點)最近的一個頂點,然後以該頂點為中心進行擴展,最終得到源點到其餘所有點的最短路徑。

基本步驟如下:

在 博客 中看到兩個比較有趣的問題,也是在學習Dijkstra時,可能會有疑問的問題。

當我們看到上面這個圖的時候,憑借多年對平面幾何的學習,會發現在「三角形ABC」中,滿足不了 構成三角形的條件(任意兩邊之和大於第三邊)。 納尼,那為什麼圖中能那樣子畫?

還是「三角形ABC」,以A為起點,B為終點,如果按照平面幾何的知識, 「兩點之間線段最短」, 那麼,A到B的最短距離就應該是6(線段AB),但是,實際上A到B的最短距離卻是3+2=5。這又怎麼解釋?

其實,之所以會有上面的疑問,是因為 對邊的權值和邊的長度這兩個概念的混淆, 。之所以這樣畫,也只是為了方便理解(每個人寫草稿的方式不同,你完全可以用別的方式表示,只要便於你理解即可)。

PS:數組實現鄰接表可能較難理解,可以看一下 這里

參考資料:

Dijkstra演算法是一種基於貪心策略的演算法。每次新擴展一個路程最短的點,更新與其相鄰的點的路程。當所有邊權都為正時,由於不會存在一個路程更短的沒擴展過的點,所以這個點的路程永遠不會再被改變,因而保證了演算法的正確性。

根據這個原理, 用Dijkstra演算法求最短路徑的圖不能有負權邊, 因為擴展到負權邊的時候會產生更短的路徑,有可能破壞了已經更新的點路徑不會發生改變的性質。

那麼,有沒有可以求帶負權邊的指定頂點到其餘各個頂點的最短路徑演算法(即「單源最短路徑」問題)呢?答案是有的, Bellman-Ford演算法 就是一種。(我們已經知道了 Floyd-Warshall 可以解決「多源最短路」問題,也要求圖的邊權均為正)

通過 鄰接矩陣 的Dijkstra時間復雜度是 。其中每次找到離 1 號頂點最近的頂點的時間復雜度是 O(N),這里我們可以用 優先隊列(堆) 來優化,使得這一部分的時間復雜度降低到 。這個我們將在後面討論。

5. 直觀理解:單源點最短路徑——Dijkstra演算法

  Dijkstra演算法是由荷蘭計算機科學家 Edsger Wybe Dijkstra於1959年提出的單源點最短路徑演算法(SSSP:Single Souce Shortest Path)。是一個解決加權圖(不含負權重的邊)中從一個頂點到其餘各個頂點最短路徑問題的演算法。Dijkstra演算法是一個集 貪心演算法 , 廣度優先搜索(BFS) 和 動態規劃 於一身的最短路徑演算法。Dijkstra演算法的主要特點是從起源點開始,採用貪心演算法的策略,每次遍歷到始點距離最近且未訪問過的頂點的鄰接頂點,直到擴展到終點為止。
  Dijkstra演算法通過維護兩個集合: (已求出最短路徑的頂點)和 (未求出最短路徑的頂點),每次迭代地從 中移除路徑距離最小的點到集合 中,並通過這個新移入的點來更新 中各個頂點到源點的最短路徑,直到集合 為空。下面我們通過一個例子來簡單描述Dijkstra演算法的過程。
  假設我們有如下的圖,其中頂點A未此次演算法的起點:

  首先我們需要初始化兩個集合 和 ,以及 中每個頂點到源點的距離,若不直接於A相鄰,結果置為正無窮∞。

   Step 1: 從集合 中挑選出距離最小的點,這里會挑選出頂點F,集合 和 變更為: , ,根據最新的 ,重新計算 中頂點到源點A的最短距離。

   Step 2:: 從集合 中挑選出距離最小的點,這里會挑選出頂點E,集合 和 變更為: , ,根據最新的 ,重新計算 中頂點到源點A的最短距離。

   Step 3: 從集合 中挑選出距離最小的點,這里會挑選出頂點C,集合 和 變更為: , ,根據最新的 ,重新計算 中頂點到源點A的最短距離。

   Step 4: 從集合 中挑選出距離最小的點,這里會挑選出頂點D,集合 和 變更為: , ,根據最新的 ,重新計算 中頂點到源點A的最短距離。

   Step 5: 從集合 中挑選出距離最小的點,這里會挑選出頂點B,集合 和 變更為: , ,根據最新的 ,重新計算 中頂點到源點A的最短距離。

   Step 6: 從集合 中挑選出距離最小的點,這里會挑選出頂點G,集合 和 變更為: , ,由於集合 為空,演算法停止迭代,輸出結果。

  以上就是對Dijkstra演算法的計算過程的簡單描述。

6. 最短路徑 - Dijkstra演算法

演算法每次都查找距離起始點最近的點,那麼剩下的點距離起始點的距離一定比當前點大。

1.選定A節點並初始化,如上述步驟3所示

2.執行上述 4、5兩步驟,找出U集合中路徑最短的節點D 加入S集合,並根據條件 if ( 'D 到 B,C,E 的距離' + 'AD 距離' < 'A 到 B,C,E 的距離' ) 來更新U集合

3.這時候 A->B, A->C 都為3,沒關系。其實這時候他倆都是最短距離,如果從演算法邏輯來講的話,會先取到B點。而這個時候 if 條件變成了 if ( 'B 到 C,E 的距離' + 'AB 距離' < 'A 到 C,E 的距離' ) ,如圖所示這時候A->B距離 其實為 A->D->B

思路就是這樣,往後就是大同小異了
演算法結束

(圖片來源於網路)

Dijkstra演算法保證能找到一條從初始點到目標點的最短路徑,只要所有的邊都有一個非負的代價值。在上圖中,粉紅色的結點是初始結點,藍色的是目標點,而類菱形的有色區域則是Dijkstra演算法掃描過的區域。顏色最淡的區域是那些離初始點最遠的,因而形成探測過程(exploration)的邊境(frontier)。因而Dijkstra演算法可以找到一條最短的路徑,但是效率上並不高。

數據結構--Dijkstra演算法最清楚的講解

7. 數學建模第四章 圖論 part4.2最短路徑問題-Dijkstra演算法

1.Dijkstra演算法介紹

演算法特點:

迪科斯徹演算法使用了廣度優先搜索解決賦權有向圖或者無向圖的單源最短路徑問題,演算法最終得到一個最短路徑樹。該演算法常用於路由演算法或者作為其他圖演算法的一個子模塊。

演算法的思路

Dijkstra演算法採用的是一種貪心的策略,聲明一個數組dis來保存源點到各個頂點的最短距離和一個保存已經找到了最短路徑的頂點的集合:T,初始時,原點 s 的路徑權重被賦為 0 (dis[s] = 0)。若對於頂點 s 存在能直接到達的邊(s,m),則把dis[m]設為w(s, m),同時把所有其他(s不能直接到達的)頂點的路徑長度設為無窮大。初始時,集合T只有頂點s。 

然後,從dis數組選擇最小值,則該值就是源點s到該值對應的頂點的最短路徑,並且把該點加入到T中,OK,此時完成一個頂點, 

然後,我們需要看看新加入的頂點是否可以到達其他頂點並且看看通過該頂點到達其他點的路徑長度是否比源點直接到達短,如果是,那麼就替換這些頂點在dis中的值。 

然後,又從dis中找出最小值,重復上述動作,直到T中包含了圖的所有頂點。

2、Dijkstra演算法示例演示

我求下圖,從頂點v1到其他各個頂點的最短路徑.

首先第一步,我們先聲明一個dis數組,該數組初始化的值為:

我們的頂點集T的初始化為:T={v1}

既然是求 v1頂點到其餘各個頂點的最短路程,那就先找一個離 1 號頂點最近的頂點。通過數組 dis 可知當前離v1頂點最近是 v3頂點。當選擇了 2 號頂點後,dis[2](下標從0開始)的值就已經從「估計值」變為了「確定值」,即 v1頂點到 v3頂點的最短路程就是當前 dis[2]值。將V3加入到T中。 

為什麼呢?因為目前離 v1頂點最近的是 v3頂點,並且這個圖所有的邊都是正數,那麼肯定不可能通過第三個頂點中轉,使得 v1頂點到 v3頂點的路程進一步縮短了。因為 v1頂點到其它頂點的路程肯定沒有 v1到 v3頂點短.

OK,既然確定了一個頂點的最短路徑,下面我們就要根據這個新入的頂點V3會有出度,發現以v3 為弧尾的有: < v3,v4 >,那麼我們看看路徑:v1–v3–v4的長度是否比v1–v4短,其實這個已經是很明顯的了,因為dis[3]代表的就是v1–v4的長度為無窮大,而v1–v3–v4的長度為:10+50=60,所以更新dis[3]的值,得到如下結果: 

因此 dis[3]要更新為 60。這個過程有個專業術語叫做「鬆弛」。即 v1頂點到 v4頂點的路程即 dis[3],通過 < v3,v4> 這條邊鬆弛成功。這便是 Dijkstra 演算法的主要思想:通過「邊」來鬆弛v1頂點到其餘各個頂點的路程。

然後,我們又從除dis[2]和dis[0]外的其他值中尋找最小值,發現dis[4]的值最小,通過之前是解釋的原理,可以知道v1到v5的最短距離就是dis[4]的值,然後,我們把v5加入到集合T中,然後,考慮v5的出度是否會影響我們的數組dis的值,v5有兩條出度:< v5,v4>和 < v5,v6>,然後我們發現:v1–v5–v4的長度為:50,而dis[3]的值為60,所以我們要更新dis[3]的值.另外,v1-v5-v6的長度為:90,而dis[5]為100,所以我們需要更新dis[5]的值。更新後的dis數組如下圖: 

然後,我們使用同樣原理,分別確定了v6和v2的最短路徑,最後dis的數組的值如下: 

因此,從圖中,我們可以發現v1-v2的值為:∞,代表沒有路徑從v1到達v2。所以我們得到的最後的結果為:

熱點內容
python跨模塊 發布:2025-07-30 23:04:43 瀏覽:300
阿泰編程 發布:2025-07-30 21:36:05 瀏覽:569
mybatis註解sqlif 發布:2025-07-30 21:33:59 瀏覽:572
安卓手機為什麼削不短下巴 發布:2025-07-30 21:23:13 瀏覽:495
澳洲訪問學者簽證類型 發布:2025-07-30 20:55:12 瀏覽:354
svn切換伺服器ip 發布:2025-07-30 20:43:10 瀏覽:198
匯通啟富軟體如何修改登錄密碼 發布:2025-07-30 20:41:08 瀏覽:243
公共場所的wifi密碼名稱是什麼 發布:2025-07-30 20:19:56 瀏覽:636
ios系統怎麼解壓 發布:2025-07-30 20:14:05 瀏覽:740
sqlip 發布:2025-07-30 19:20:22 瀏覽:178