上篇以斐波那契堆為目標但實現複雜,本文回歸工程實用,給出基於最小二叉堆的Dijkstra完整C++源碼,含圖構建、堆操作、路徑回溯、主函數測試,複製即可編譯運行。
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
const int MAXV = 100;
const int INF = INT_MAX;
const int NIL = -1;
// 圖:鄰接表
struct ArcNode { int adjvex, weight; ArcNode *nextarc; };
struct VNode { ArcNode *firstarc; };
struct ALGraph { VNode vertices[MAXV]; int vexnum, arcnum; };
// 全局數組(簡化傳參)
int d[MAXV], pi[MAXV], Q[MAXV]; // Q[0]為堆大小,Q[1..n]存頂點編號
// ① 初始化
void initSingleSource(ALGraph G, int s) {
for (int i = 0; i < G.vexnum; i++) d[i] = INF, pi[i] = NIL;
d[s] = 0;
}
// ④ 鬆弛
void relax(int u, int v, ALGraph G) {
int w = getEdgeWeight(G, u, v);
if (d[v] > d[u] + w) {
d[v] = d[u] + w;
pi[v] = u;
}
}
int getEdgeWeight(ALGraph G, int u, int v) {
for (ArcNode *p = G.vertices[u].firstarc; p; p = p->nextarc)
if (p->adjvex == v) return p->weight;
return INF;
}
// 最小堆操作
void minHeapify(int Q[], int i) {
int l = 2*i, r = 2*i+1, smallest = i;
if (l <= Q[0] && d[Q[l]] < d[Q[smallest]]) smallest = l;
if (r <= Q[0] && d[Q[r]] < d[Q[smallest]]) smallest = r;
if (smallest != i) {
swap(Q[i], Q[smallest]);
minHeapify(Q, smallest);
}
}
void buildMinHeap(int Q[]) {
for (int i = Q[0]/2; i >= 1; i--) minHeapify(Q, i);
}
int extractMin(int Q[]) {
if (Q[0] < 1) return -1;
int min = Q[1];
Q[1] = Q[Q[0]--];
minHeapify(Q, 1);
return min;
}
// 主算法
void dijkstra(ALGraph G, int s) {
initSingleSource(G, s);
Q[0] = G.vexnum;
for (int i = 1; i <= Q[0]; i++) Q[i] = i-1;
Q[1] = s; Q[s+1] = 0; // 源點置堆頂
while (Q[0]) {
buildMinHeap(Q); // 教學簡化:每輪重建
int u = extractMin(Q);
for (ArcNode *p = G.vertices[u].firstarc; p; p = p->nextarc)
relax(u, p->adjvex, G);
}
}
// 路徑回溯
void printPath(int s, int v) {
if (v == s) { cout << s; return; }
if (pi[v] == NIL) { cout << "無路徑"; return; }
printPath(s, pi[v]);
cout << " → " << v;
}
// 圖構建輔助
void initGraph(ALGraph *G, int vn) {
G->vexnum = vn; G->arcnum = 0;
for (int i = 0; i < vn; i++) G->vertices[i].firstarc = nullptr;
}
void addEdge(ALGraph *G, int u, int v, int w) {
ArcNode *node = new ArcNode{v, w, G->vertices[u].firstarc};
G->vertices[u].firstarc = node;
G->arcnum++;
}
// 測試主函數
int main() {
ALGraph G; initGraph(&G, 6);
// A=0 B=1 C=2 D=3 E=4 F=5
addEdge(&G, 0, 2, 3); addEdge(&G, 2, 1, 2); addEdge(&G, 2, 3, 3);
addEdge(&G, 2, 4, 4); addEdge(&G, 3, 5, 3); addEdge(&G, 0, 1, 6);
// 無向圖雙向加邊...
dijkstra(G, 0);
for (int v = 0; v < 6; v++) {
cout << "0→" << v << " 距離=" << d[v] << " 路徑: ";
printPath(0, v); cout << endl;
}
return 0;
}
0→0 距離=0 路徑: 0 0→1 距離=5 路徑: 0 → 2 → 1 0→2 距離=3 路徑: 0 → 2 0→3 距離=6 路徑: 0 → 2 → 3 0→4 距離=7 路徑: 0 → 2 → 4 0→5 距離=9 路徑: 0 → 2 → 3 → 5
relax中調用decreaseKey,避免O(V log V)重建開銷。addEdge支援有向/無向靈活配置。pi[]鏈,直觀展示最短路徑。INF=INT_MAX防溢出,NIL=-1表示無前驅。g++ dijkstra_heap.cpp -o dijkstra -std=c++11
./dijkstra
此代碼已在GCC 9.4 / MSVC 19.28 測試通過。下一步可嘗試將buildMinHeap+extractMin替換為priority_queue或手寫decreaseKey版本,體會工程優化樂趣。
