前面在介紹並查集時順便提了Kruskal算法,既然已經說到了最小生成樹問題,就沒有道理不把Prime算法說了。
這里面先補充下Kruskal算法的大概意思,Kruskal算法通過把所有的邊從小到大排列后,不斷取權值最小的邊加入最小生成樹(起初可能是離散的多個樹,最終連成一個整體),並通過並查集來舍棄形成回路的邊。
Prime算法有所不同,Prime算法先將一個起點加入最小生成樹,之后不斷尋找與最小生成樹相連的邊權最小的邊能通向的點,並將其加入最小生成樹,是一種更符合人的主觀直覺的最小生成樹算法。
需要注意的是,Kruskal和Prime都僅適用於無向圖。
#include <algorithm> const int MAX_V = 100; const int INF = 1000000; int cost[MAX_V][MAX_V];//圖 int V; bool path[MAX_V][MAX_V];//記錄結果 int res; bool used[MAX_V];//表示是否訪問過 int mincost[MAX_V];//表示到此點消耗值 void Prime() { res = 0;//統計最小消耗 for (int i = 0;i < V;++i)//初始化 { used[i] = false; mincost[i] = INF; for (int j = 0;j < V;++j) { path[i][j] = false; } } mincost[0] = 0;//從0點開始 int prev = 0;//記錄路徑 while (true) { int visited = -1; for (int i = 0;i < V;++i) { if (!used[i] && (visited == -1 || mincost[i] < mincost[visited])) visited = i;//貪婪尋找最短邊 } if (visited == -1) break; used[visited] = true; if (visited) { path[prev][visited] = true; prev = visited; } res += mincost[visited]; for (int i = 0;i < V;++i) { mincost[i] = std::min(mincost[i], cost[visited][i]); } } }
下面給出一組Kruskal和Prime的測試數據及結果:
測試代碼:
void mstTest() { cin >> V; for (int i = 0;i < V;++i) for (int j = 0;j < V;++j) { cin >> cost[i][j]; } for (int i = 0;i < V;++i) d[i] = INF; cout << "Kruskal:" << endl; Kruskal(); for (int i = 0;i < V;++i) { for (int j = 0;j < V;++j) { if (path[i][j]) cout << i << " " << j << endl; } } cout << res << endl; cout << endl; cout << "Prime" << endl; Prime(); for (int i = 0;i < V;++i) { for (int j = 0;j < V;++j) { if (path[i][j]) cout << i << " " << j << endl; } } cout << res << endl; }
測試結果:
9
1000000 1 5 7 4 1000000 1000000 1000000 1000000
1 1000000 1000000 1000000 1000000 3 10 1000000 1000000
5 1000000 1000000 1000000 1000000 2 1000000 2 1000000
7 1000000 1000000 1000000 1000000 1000000 1 1000000 1000000
4 1000000 1000000 1000000 1000000 1000000 1000000 3 1000000
1000000 3 2 1000000 1000000 1000000 1000000 1000000 2
1000000 10 1000000 1 1000000 1000000 1000000 1000000 9
1000000 1000000 2 1000000 3 1000000 1000000 1000000 5
1000000 1000000 1000000 1000000 1000000 2 9 5 1000000
Kruskal:
0 1
0 3
1 5
2 5
2 7
3 6
4 7
5 8
21
Prime
0 1
1 5
2 7
3 6
4 3
5 2
7 8
8 4
21
請按任意鍵繼續. . .
可以看到,兩種算法生成了擁有相同最小消耗的兩顆完全不同的最小生成樹。