大話數據結構學習筆記 - 圖的最短路徑之Dijkstra
算法
最短路徑
最短路徑是圖中的重要問題,對於網圖和非網圖來說,最短路徑的含義也是不同的。由於非網圖沒有邊上的權值,所謂的最短路徑,其實就是指兩頂點之間經過的邊數最少的路徑。而對於網圖來說,最短路徑,是指兩頂點之間經過的邊上的權值之和最少的路徑,並且我們稱路徑上的第一個頂點是源點,最後一個頂點是終點。當然非網圖可以理解爲所有邊的權值都爲1
的網
迪傑斯塔拉(Dijkstra
)算法
迪傑斯特拉(Dijkstra
)算法是典型的最短路徑算法,用於計算一個結點到其他結點的最短路徑。算法的主要特點是以起始點爲中心向外層層擴展(廣度優先搜索思想), 直到擴展到終點爲止
基本思想
通過Dijkstra
計算圖G
中的最短路徑時,需要指定起點v0
, 即從v0
點開始計算。 起初,設置S
數組表示已求得最短路徑的頂點, U
數組表示起點到各頂點的最短距離, 即起點到各頂點最短路徑的權值和。P
數組表示最短路徑中的各頂點
算法圖解
以上圖左圖爲例進行算法演示,右圖爲構建的無向圖鄰接矩陣。final
數組爲已求得最短路徑的頂點集合,D
數組爲起點到各頂點的最短路徑的權值和, P
數組爲最短路徑的路徑的頂點。
初始狀態: final
數組表示起點v0
到某頂點是否已求得最短路徑的標記, 即若v0
到vw
已有結果,則final[w]=1
。起初final
數組均爲0
, D
數組爲{65535, 1, 5, 65535, 65535, 65535, 65535, 65535, 65535}
第一步 :將起始點v0
加入到final
數組, final[0] = 1
, 此時final
數組爲{1, 0, 0, 0, 0, 0, 0, 0, 0}
, 且v0
到v0
路徑爲0
. D
數組爲{0, 1, 5, 65535, 65535, 65535, 65535, 65535, 65535}
第二步: 尋找與final
數組中已有頂點的最短距離, 並更新D
數組。 與v0
相連的頂點最短距離爲v1
頂點,權值爲1
, 故將v1
加入到final
數組。 final = {1,1,0,0,0,0,0,0,0,0}
第三步: 對更新後的final
數組進行路徑權值和的更新, 即D
的更新 , 在原來的權值和基礎上,更新新加入的頂點與其他各頂點得到權值和,比如final
數組新增加v1
頂點, 對D
數組進行更新後, D = {0, 1, 4, 8, 6, 65535, 65535, 65535, 65535}
第四步: 尋找與final
集合權值最短的頂點, 頂點v0、v1
不參與, 故爲頂點v2
, 距離爲4, 然後更新final
數組和D
數組 final = {1,1,1,0,0,0,0,0,0}
, D = {0,1,4,8,5,11,65535, 65535, 65535}
, 此時P
數組爲P = {0,0,1,1,2,2,0,0,0}
循環依次繼續, 最後final
數組包含所有頂點, D
數組包含起點到其餘所有頂點的最短距離, P
數組包含起點到任意頂點的路徑長度。即迪傑斯塔拉算法解決了從某個源點到其餘各頂點的最短路徑問題
代碼
無向圖結構
#define MAXVEX 10
#define INF 65535
typdef int Patharc[MAXVEX]; // 用於存儲最短路徑下標的數組
typedef int ShortPathTable[MAXVEX]; // 用於存儲起點到各點最短路徑的權值和
typedef struct
{
int vexs[MAXVEX];
int arc[MAXVEX][MAXVEX];
int numVertexes, numEdges;
}MGraph;
Dijkstra
算法
/* Dijkstra算法, 求有向網 G 的 v0 頂點到其餘頂點 v 最短路徑 P[v] 及帶權長度 D[v], P[v] 的值爲前驅頂點下標,
* D[v] 表示 v0 到 v 的最短路徑長度和 */
void ShortestPath_Dijkstra(MGraph G, int v0, ShortPathTable *D, Patharc *P)
{
int min, k, final[MAXVEX]; /* final[w] = 1 表示求得頂點 v0 至 vw 的最短路徑 */
for(int v = 0; v < G.numVertexes; v++) /* 初始化數據 */
{
final[v] = 0; /* 全部頂點初始化爲未知最短路徑狀態 */
(*D)[v] = G.arc[v0][v]; /* 將與 v0 頂點有連線的頂點加上權值 */
(*P)[v] = -1; /* 初始化路徑數組 P 爲 0 */
}
final[v0] = 1; /* v0 至 v0 不需要求路徑 */
(*D)[v0] = 0; /* v0 至 v0 路徑爲 0 */
/* 開始主循環, 每次求得 v0 到某個 v 頂點的最短路徑 */
for(int v = 1; v < G.numVertexes; v++)
{
min = INF; /* 當前所知離 v0 頂點的最近距離 */
for(int w = 0; w < G.numVertexes; w++) /* 尋找離 v0 最近的頂點 */
{
if(!final[w] && (*D)[w] < min)
{
min = (*D)[w]; /* w 頂點離 v0 頂點更近 */
k = w;
}
}
final[k] = 1; /* 將目前找到的最近的頂點置爲 1 */
for(int w = 0; w < G.numVertexes; w++) /* 更新當前最短路徑及距離 */
{
/* 如果經過 v 頂點的路徑比現在這條路徑的長度短的話 */
if(!final[w] && (min + G.arc[k][w]) < (*D)[w])
{
/* 說明找到了更短的路徑, 修改 D[w] 和 P[w] */
(*D)[w] = min + G.arc[k][w]; /* 修改當前路徑長度 */
(*P)[w] = k;
}
}
}
}