--- tags: APCS --- # Kruskal 演算法 ``` graphviz graph G { splines="line" node[shape=circle] {rank=same;1,2} {rank=same;3,4,5,6} 0 -- 1 [label=3] 0 -- 2 [label=1] 1 -- 3 [label=1] 1 -- 5 [label=1] 2 -- 5 [label=7] 2 -- 6 [label=5] 3 -- 4 [label=2] 4 -- 5 [label=1] } ``` ``` cpp= // // Minimum Cost Spanning Tree - Kruskal // // 依序挑選 n-1 條不會造成 loop 的最小邊 // #include <iostream> #include <vector> #include <string> #include <sstream> #include <queue> using namespace std; //--------------------------------------------------------------------------- // 測試資料 // // 第一行,一個整數 n 表示有幾個節點 // 剩下一直到結尾的每一行的 3 個整數都代表一條邊,格式為 v1 v2 w // v1 v2 代表線的兩個端點, // w 代表這條線的權重(或成本) // string input = "\ 7\n\ 0 1 3\n\ 0 2 1\n\ 1 3 1\n\ 1 5 1\n\ 2 5 7\n\ 2 6 5\n\ 3 4 2\n\ 4 5 1\n"; // Kruskal 演算法主要以邊為考量,所以將邊的兩端都放在結構裡 struct Edge { int from, to, weight; }; // 重載 < 運算子,定義如何比較兩個 Edge 變數 // priority_queue<Edge> 會自動調用這個運算子,將優先權高的排在前面 bool operator<(const Edge &e1, const Edge &e2) { // weight 高的優先權較小 return e1.weight > e2.weight; } // 用結構紀錄圖的組態 struct Graph { int n; vector<Edge> edge; }; // 以往上找 root 的方式找到群組的頭 (parent == 自己) int group(vector<int> &parent, int n) { for (; parent[n] != n; n = parent[n]) ; return n; } int mst_kruskal(Graph g) { vector<int> parent(g.n); // 每個點的 parent,往上追到 root 來比對是否為同一個群組 priority_queue<Edge> pq; // 挑選邊用的優先佇列,會依優先權高至低的順序自動排列元素 int remain_edges = g.n - 1, // 只要挑完 n-1 條邊就可以了 min_cost = 0; for (int i = 0; i < g.n; i++) parent[i] = i; // 先將每個點的 parent 設為自己(每個點的群組頭就是自己) for (auto &e: g.edge) { // 將圖裡所有的邊都丟進優先佇列中等待處理 pq.push(e); } while (remain_edges > 0 && !pq.empty()) { auto e = pq.top(); pq.pop(); // 依次挑出優先權最高(成本最小)的邊 int g1 = group(parent, e.from), g2 = group(parent, e.to); if (g1 != g2) { // 兩端不在同一群組的邊才可以挑,不然會 loop parent[g1] = g2; // 合併群組(將群組的 parent 指向另一個群首就可以了) min_cost += e.weight; remain_edges--; } } return min_cost; } int main() { stringstream sin(input); Graph G; int a, b, r; sin >> G.n; while (sin >> a >> b >> r) { G.edge.push_back({a, b, r}); } cout << mst_kruskal(G) << '\n'; return 0; } ```