1. 解決問題
目的是求某一頂點到其餘各個頂點【最短路徑】。
2. Dijkstra算法思想
假設一共有兩個頂點集合 S 和 T ,集合 S 中存放圖中已找到最短路徑的頂點,集合 T 中存放圖中的剩餘頂點。
初始狀態時,S 中只包含源點 V0,然後不斷通過從集合 T 中選取到頂點 V0 路徑長度最短的頂點 Vu 併入集合 S 中。
集合 S 每併入一個新的頂點Vu ,都要修改頂點 V0 到集合 T 中頂點的最短路徑長度值。
不斷重複這個過程,直到所有頂點全部併入集合 S 中爲止。
3. 輔助數據結構
Set T[ ]:已找到最短路徑的結點集合。這裏爲了簡單起見用 int T[ ] 表示,當 T[i]==1 表示存在上述集合 S 中,T[i]==0 表示存在上述集合 T 中。
int pre[ ]:記錄V0 到 Vi 的前驅結點 Vi-1。
int dist[ ]:記錄V0 到 Vi 的最短路徑的長度。
4. 圖的表示
這裏採用簡單的鄰接矩陣表示有向圖。
int graph[7][7] = {
{0, 4, 6, 6, 0, 0, 0},
{0, 0, 1, 0, 7, 0, 0},
{0, 0, 0, 0, 6, 4, 0},
{0, 0, 2, 0, 0, 5, 0},
{0, 0, 0, 0, 0, 0, 6},
{0, 0, 0, 0, 1, 0, 8},
{0, 0, 0, 0, 0, 0, 0}
};
5. 代碼描述
5.1 judge()
判斷T中是否包含所有的頂點,如果是Set類型則不需要此函數,只需要 set.find(i) 即可。
bool judge(int n) {
for (int i = 0; i < n; ++i) {
if (T[i] == 0)
return false;
}
return true;
}
5.2 minIndex()
返回 dist[ ] 數組中所有路徑距離最小的頂點下標。
int minIndex(int n) {
int index = -1;
int min = INT_MAX;
for (int i = 0; i < n; i++) {
if (T[i] == 0 && dist[i] < min) {
min = dist[i];
index = i;
}
}
return index;
}
5.3 shortestPath()
第一步:直接可達的頂點,用距離來初始化dist,dist[start]=0,可直達的把距離記錄下來作爲待定值。
第二步:從待定的距離表中找到最小值min以及對應的頂點 M ,這個值可以作爲確定值,爲什麼?(因爲不可能會有圖中任何一個頂點 x ,由 start 經過 x 再到頂點 M 產生的距離,要比直接從 start 到 M 更小)
第三步:看這個新確定的頂點的出度,看看從源點出發是經過這個頂點到其鄰居近還是直達更近,如果更近就要更新。
void shortestPath(int start, int n) {
pre[start] = start;
dist[start] = 0;
T[start] = 1;
//step1
for (int i = 0; i < n; i++) {
if (i != start) {
if (graph[start][i] == 0)
dist[i] = INT_MAX;
else if (graph[start][i] > 0) {
dist[i] = graph[start][i];
pre[i] = start;
}
}
}
while (judge(n) == false) {
//step2
int index = minIndex(n);
T[index] = 1;
if (judge(n) == true)
break;
//step3
for (int neighbor = 0; neighbor < n; neighbor++) {
int weight = graph[index][neighbor];
if (weight > 0 && (dist[index] + weight) < dist[neighbor]) {
dist[neighbor] = dist[index] + weight;
pre[neighbor] = index;
}
}
}
}
5.4 main()
應該倒着輸出 pre[ ] ,因爲存的是前驅結點,也可以用棧處理一下。
int main() {
int n = sizeof(graph[0]) / sizeof(int);
shortestPath(0, n);
for (int i = 0; i < n; ++i) {
cout << dist[i] << " ";
}
cout << endl;
for (int i = 0; i < n; ++i) {
cout << pre[i] << " ";
}
}
6.總結
Dijkstra算法是圖論中經典的求最短路徑的算法,筆者在學習過程中發現它運用的是動態規劃的思想,此外圖論還有拓撲排序,Prim、Kruskal求最小生成樹算法、求任意兩點間的最短路徑的Floyd算法,以及求關鍵路徑的算法,這些對於考研和工作來說都是必須掌握的數據結構算法的基本知識,希望與大家共勉!