最短路徑問題的經典解法-dijsktra演算法
問題描述:求從一個頂點到另一個頂點的最短路徑
【演算法設計思想】
Dijkstra演算法的設計思想基於以下關鍵概念和步驟,旨在找出圖中從一個給定的源頂點到其他所有頂點的最短路徑。這個演算法適用於有向和無向圖,只要圖的邊權重為非負值。
1. 初始化
- 設
dist[]
為一個陣列,dist[i]
表示從源頂點到頂點i
的最短路徑估計。初始時,對所有i
,dist[i]
設為無窮大,除了源頂點自身,其值設為0。 - 設
SPTSet[]
(最短路徑樹集合)為一個集合,用於記錄已經被處理並且其最短路徑已經被找到的頂點。最開始,這個集合為空。
2. 選擇最小距離頂點
- 在未處理的頂點集合中選擇一個距離最小的頂點
u
,即dist[u]
是最小的,並且u
不在SPTSet
中。最開始,這將會是源頂點。
3. 更新距離值
- 對於頂點
u
的每個鄰接頂點v
,如果v
不在SPTSet
中,並且透過u
到v
的路徑長度小於當前記錄的dist[v]
的值,則更新dist[v]
。更新規則是:dist[v] = dist[u] + weight(u, v)
,其中weight(u, v)
是從u
到v
的邊的權重。
4. 標記為已處理
- 將頂點
u
新增到SPTSet
中,表示u
的最短路徑已經被找到。
5. 重複步驟
- 重複步驟2至4,直到所有頂點都被處理,即
SPTSet
包含所有頂點。
【演算法描述】
// 尋找最短路徑樹集合中距離最小的頂點
int minDistance(int dist[], int sptSet[]) {
// 初始化最小值
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++)
if (sptSet[v] == 0 && dist[v] <= min)
min = dist[v], min_index = v;
return min_index;
}
// 使用Dijkstra演算法找到圖中源點到所有頂點的最短路徑
void dijkstra(int graph[V][V], int src) {
int dist[V]; // dist[i]會儲存源點到i的最短路徑距離
int sptSet[V]; // sptSet[i]會為真如果頂點i在最短路徑樹中,或者最短距離從源點到i已經確認
// 初始化所有距離為無窮大,sptSet[]為假
for (int i = 0; i < V; i++)
dist[i] = INT_MAX, sptSet[i] = 0;
// 源點到自己的距離總是0
dist[src] = 0;
// 尋找所有頂點的最短路徑
for (int count = 0; count < V - 1; count++) {
// 從尚未處理的頂點集合中選取距離最小的頂點
int u = minDistance(dist, sptSet);
// 標記選取的頂點為已處理
sptSet[u] = 1;
// 更新選取頂點的鄰接頂點的距離值
for (int v = 0; v < V; v++)
// 更新dist[v]只有在以下情況:未在sptSet中,存在從u到v的邊,且源點到v的總距離小於當前dist[v]的值
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v])
dist[v] = dist[u] + graph[u][v];
}
// 列印構建的距離陣列
printSolution(dist);
}
【完整的測試程式】
#include <stdio.h>
#include <limits.h>
// 頂點數量
#define V 9
// 尋找最短路徑樹集合中距離最小的頂點
int minDistance(int dist[], int sptSet[]) {
// 初始化最小值
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++)
if (sptSet[v] == 0 && dist[v] <= min)
min = dist[v], min_index = v;
return min_index;
}
// 列印構建的距離陣列
void printSolution(int dist[]) {
printf("頂點 \t 距離源點的距離\n");
for (int i = 0; i < V; i++)
printf("%d \t %d\n", i, dist[i]);
}
// 使用Dijkstra演算法找到圖中源點到所有頂點的最短路徑
void dijkstra(int graph[V][V], int src) {
int dist[V]; // dist[i]會儲存源點到i的最短路徑距離
int sptSet[V]; // sptSet[i]會為真如果頂點i在最短路徑樹中,或者最短距離從源點到i已經確認
// 初始化所有距離為無窮大,sptSet[]為假
for (int i = 0; i < V; i++)
dist[i] = INT_MAX, sptSet[i] = 0;
// 源點到自己的距離總是0
dist[src] = 0;
// 尋找所有頂點的最短路徑
for (int count = 0; count < V - 1; count++) {
// 從尚未處理的頂點集合中選取距離最小的頂點
int u = minDistance(dist, sptSet);
// 標記選取的頂點為已處理
sptSet[u] = 1;
// 更新選取頂點的鄰接頂點的距離值
for (int v = 0; v < V; v++)
// 更新dist[v]只有在以下情況:未在sptSet中,存在從u到v的邊,且源點到v的總距離小於當前dist[v]的值
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v])
dist[v] = dist[u] + graph[u][v];
}
// 列印構建的距離陣列
printSolution(dist);
}
int main() {
// 例子中圖的鄰接矩陣表示法
int graph[V][V] = {{0, 4, 0, 0, 0, 0, 0, 8, 0},
{4, 0, 8, 0, 0, 0, 0, 11, 0},
{0, 8, 0, 7, 0, 4, 0, 0, 2},
{0, 0, 7, 0, 9, 14, 0, 0, 0},
{0, 0, 0, 9, 0, 10, 0, 0, 0},
{0, 0, 4, 14, 10, 0, 2, 0, 0},
{0, 0, 0, 0, 0, 2, 0, 1, 6},
{8, 11, 0, 0, 0, 0, 1, 0, 7},
{0, 0, 2, 0, 0, 0, 6, 7, 0}};
dijkstra(graph, 0);
return 0;
}