單源最短路徑 迪傑斯特拉演算法

2021-08-08 22:35:05 字數 3508 閱讀 8815

dijkstra 演算法(中文名:迪傑斯特拉演算法)是由荷蘭計算機科學家 edsger wybe dijkstra 提出。該演算法常用於路由演算法或者作為其他圖演算法的乙個子模組。舉例來說,如果圖中的頂點表示城市,而邊上的權重表示城市間開車行經的距離,該演算法可以用來找到兩個城市之間的最短路徑。

我們用乙個例子來具體說明迪傑斯特拉演算法的流程。

定義源點為 0,dist[i]為源點 0 到頂點 i 的最短路徑。其過程描述如下:

步驟dist[1]

dist[2]

dist[3]

dist[4]

已找到的集合

第 1 步81

2+∞第 2 步8×

24第 3 步5×

×4第 4 步5×

××第 5 步××

××第 1 步:從源點 0 開始,找到與其鄰接的點:1,2,3,更新dist陣列,因 0 不與 4 鄰接,故dist[4]為正無窮。在dist中找到最小值,其頂點為 2,即此時已找到 0 到 2 的最短路。

第 2 步:從 2 開始,繼續更新dist陣列:2 與 1 不鄰接,不更新;2 與 3 鄰接,因0→2→3dist[3]大,故不更新dist[3];2 與 4 鄰接,因0→2→4dist[4]小,故更新dist[4]為 4。在dist中找到最小值,其頂點為 3,即此時又找到 0 到 3 的最短路。

第 3 步:從 3 開始,繼續更新dist陣列:3 與 1 鄰接,因0→3→1dist[1]小,更新dist[1]為 5;3 與 4 鄰接,因0→3→4dist[4]大,故不更新。在dist中找到最小值,其頂點為 4,即此時又找到 0 到 4 的最短路。

第 4 步:從 4 開始,繼續更新dist陣列:4 與 1 不鄰接,不更新。在dist中找到最小值,其頂點為 1,即此時又找到 0 到 1 的最短路。

第 5 步:所有點都已找到,停止。

對於上述步驟,你可能存在以下的疑問:

若 a 作為源點,與其鄰接的只有 b,c,d 三點,其dist最小時頂點為 c,即就可以確定a→c為 a 到 c 的最短路。但是我們存在疑問的是:是否還存在另一條路徑使 a 到 c 的距離更小? 用反證法證明。

假設存在如上圖的紅色虛線路徑,使a→d→c的距離更小,那麼a→d作為a→d→c的子路徑,其距離也比a→c小,這與前面所述 「dist最小時頂點為 c」 矛盾,故假設不成立。因此這個疑問不存在。

根據上面的證明,我們可以推斷出,dijkstra 每次迴圈都可以確定乙個頂點的最短路徑,故程式需要迴圈 n-1 次。

/**

* * author : 劉毅(limer)

* date : 2017-05-17

* mode : c++

*/#include

using

namespace

std;

int matrix[100][100]; // 鄰接矩陣

bool visited[100]; // 標記陣列

int dist[100]; // 源點到頂點i的最短距離

int path[100]; // 記錄最短路的路徑

int source; // 源點

int vertex_num; // 頂點數

int side_num; // 邊數

void

dijkstra

(int source)

int min_cost; // 權值最小

int min_cost_index; // 權值最小的下標

for (int i = 1; i < vertex_num; i++) // 找到源點到另外 vertex_num-1 個點的最短路徑

}visited[min_cost_index] = true; // 該點已找到,進行標記

for (int j = 0; j < vertex_num; j++) // 更新 dist 陣列}}

}int

main

() cout

<< "請輸入源點(

<< vertex_num << "):";

cin >> source;

dijkstra(source);

for (int i = 0; i < vertex_num; i++)

cout

<< "--"

<< source << endl;}}

return

0;}

輸入資料,結果為:

設圖的邊數為 m,頂點數為 n。

dijkstra 演算法最簡單的實現方法是用乙個陣列來儲存所有頂點的dist(即本程式採用的策略),所以搜尋dist中最小元素的運算需要線性搜尋 o(n)

。這樣的話演算法的執行時間是 o(n2)

。對於邊數遠少於 n2

的稀疏圖來說,我們可以用鄰接表來更有效的實現該演算法。同時需要將乙個二叉堆或者斐波納契堆用作優先佇列來查詢最小的頂點。當用到二叉堆的時候,演算法所需的時間為 o((m+n)logn)

,斐波納契堆能稍微提高一些效能,讓演算法執行時間達到 o(m+nlogn)

。然而,使用斐波納契堆進行程式設計,常常會由於演算法常數過大而導致速度沒有顯著提高。

關於 o((m+n)logn)

的由來,我簡單的證明了下(僅個人看法,不保證其正確性):

綜上所述:總的時間複雜度為:o(n)+o(nlogn)+o(mlogn)=o((m+n)logn)

最後簡單說下 dijkstra 優化時二叉堆的兩種實現方式:

相比之下,前者的編碼難度較低,因此在平時程式設計甚至演算法競賽中,都是首選。

dijkstra 演算法有個巨大的缺陷,請考慮下面這幅圖:

u→v間存在一條負權迴路(所謂的負權迴路,維基和百科並未收錄其名詞,但從網上的一致態度來看,其含義為:如果存在乙個環(從某個點出發又回到自己的路徑),而且這個環上所有權值之和是負數,那這就是乙個負權環,也叫負權迴路),那麼只要無限次地走這條負權迴路,便可以無限制地減少它的最短路徑權值,這就變相地說明最短路徑不存在。乙個不存在最短路徑的圖,dijkstra 演算法無法檢測出這個問題,其最後求解的dist也是錯的。

那麼對於上述的 「乙個不存在最短路徑的圖」,我們該用什麼方法來解決呢?請接著看本系列第二篇文章。

最短路徑 迪傑斯特拉演算法

例如,要求下圖v0到v8的最短路徑 所以我們可以找到這樣的一條最短路徑 下面是他的鄰接矩陣 偽 如下 define maxvex 9 define infinity 65535 typedef int patharc maxvex 用於儲存最短路徑下標的陣列 typedef int shortpat...

迪傑斯特拉最短路徑演算法

時間限制 1 sec 記憶體限制 32 mb 提交 27 解決 17 提交 狀態 命題人 外部匯入 題目描述 在帶權有向圖g中,給定乙個源點v,求從v到g中的其餘各頂點的最短路徑問題,叫做單源點的最短路徑問題。在常用的單源點最短路徑演算法中,迪傑斯特拉演算法是最為常用的一種,是一種按照路徑長度遞增的...

最短路徑(迪傑斯特拉演算法)

源 include define maxint 32767 表示極大值 define mvnum 100 最大頂點數 typedef char vertextype 定義資料型別 typedef int arctype typedef struct amgraph int locatevex amg...