單源最短路徑演算法,適用於邊權非負的有向/無向圖。從固定源點出發,逐步擴展已確定最短路徑的頂點集合。
#define MAXN 20
#define INF 1000000
int n; // 頂點數
int Edge[MAXN][MAXN]; // 鄰接矩陣
int S[MAXN]; // 集合標記:1=已加入最短路徑集合
int dist[MAXN]; // 源點到各頂點當前最短距離
int path[MAXN]; // 路徑前驅頂點
dist[i] = Edge[v0][i],S[v0]=1,path[i]=v0 (可達) 或 -1 (不可達)dist 最小者 u 加入 Sk,若經 u 更短則更新 dist[k] 和 path[k]
void Dijkstra(int v0) {
int i, j, k;
// 步驟1:初始化
for (i = 0; i < n; i++) {
dist[i] = Edge[v0][i];
S[i] = 0;
if (i != v0 && dist[i] < INF)
path[i] = v0;
else
path[i] = -1;
}
S[v0] = 1;
dist[v0] = 0; // 關鍵:源點距離歸零
// 步驟2-3:重複 n-1 次
for (i = 0; i < n-1; i++) {
// 找未加入集合中 dist 最小的頂點 u
int min = INF, u = v0;
for (j = 0; j < n; j++)
if (!S[j] && dist[j] < min) {
u = j;
min = dist[j];
}
S[u] = 1; // 加入集合
// 鬆弛操作
for (k = 0; k < n; k++)
if (!S[k] && Edge[u][k] < INF && dist[u] + Edge[u][k] < dist[k]) {
dist[k] = dist[u] + Edge[u][k];
path[k] = u;
}
}
}
int main() {
int i, j, u, v, w;
scanf("%d", &n);
// 讀取邊:u v w,以 -1 -1 -1 結束
while (1) {
scanf("%d %d %d", &u, &v, &w);
if (u==-1 && v==-1 && w==-1) break;
Edge[u][v] = w;
}
// 初始化鄰接矩陣
for (i = 0; i < n; i++)
for (j = 0; j < n; j++) {
if (i == j) Edge[i][j] = 0;
else if (Edge[i][j] == 0) Edge[i][j] = INF;
}
Dijkstra(0); // 以 0 為源點
// 輸出最短距離與路徑
for (i = 1; i < n; i++) {
printf("%d\t", dist[i]);
int shortest[MAXN] = {0};
int k = 0;
shortest[k] = i;
while (path[shortest[k]] != 0) {
k++;
shortest[k] = path[shortest[k-1]];
}
k++;
shortest[k] = 0;
for (j = k; j > 0; j--)
printf("%d->", shortest[j]);
printf("%d\n", shortest[0]);
}
return 0;
}
輸入: 6 0 1 10 0 2 5 1 2 2 1 3 1 2 1 3 2 3 9 2 4 2 3 4 4 4 3 6 4 5 2 -1 -1 -1 輸出: 7 0->2->1 9 0->2->1->3 5 0->2 11 0->2->4 9 0->2->4->5
| 實作方式 | 時間複雜度 | 空間複雜度 | 適用場景 |
|---|---|---|---|
| 鄰接矩陣 + 線性搜尋 | O(V²) | O(V²) | 稠密圖 (E ≈ V²) |
| 鄰接表 + 二元堆 | O((V+E) log V) | O(V+E) | 稀疏圖 (E ≪ V²) |
| 鄰接表 + 費波納希堆 | O(E + V log V) | O(V+E) | 理論最優 |