#include <algorithm> class Solution { public: /** n 户人家 → n 个点(顶点) m 条路 → m 条边,每条边有一个权重(修路成本) 要让所有人家连起来(形成连通图),而且总成本最低 → 最小生成树(Minimum Spanning Tree, MST) 生成树:包含所有顶点,且n-1条边,也就是一条线连接所有顶点; 最小生成树:采用的边的代价和最小 Kruskal 的核心思想是一个贪心策略: 每次挑最便宜的路(按边的距离排序),利用并查集, 如果两个节点还没有连接(不在一个集合里),就修它(放到一个集合,累计成本),直到所有村庄连通(n-1个边被使用)。 */ struct UnionFind{ vector<int> parent, rank; UnionFind(int n){ parent.resize(n); //对头节点进行初始化 rank.resize(n, 0); //对集合的高度进行初始化 for(int i=0; i<n; i++){ parent[i] = i; //每个的头节点初始化为自己,表示自己是一个集合 } } int find(int x){ //查找数x的头节点是多少,并进行路径压缩(找到后赋值) if(parent[x] != x){ //若是下属 parent[x] = find(parent[x]); //如果 } return parent[x]; } // 合并两个集合 bool unite(int x, int y){ int root_x = find(x); int root_y = find(y); if(root_x == root_y)return false; //两个城市已经在同一个集合里了,不需要合并 // 把树高度低的那部分,放到高的集合下 if(rank[root_x] < rank[root_y]){ parent[root_x] = root_y; //改变头节点即可 }else if(rank[root_y] < rank[root_x]){ parent[root_y] = root_x; }else{ parent[root_y] = root_x; //一样高时,作为两个集合的头的高度+1; rank[root_x]++; } return true; } }; int miniSpanningTree(int n, int m, vector<vector<int> >& cost) { sort(cost.begin(), cost.end(), [](vector<int>& a, vector<int>& b){ return a[2] < b[2]; }); //按照成本从小到大排序 UnionFind uf(n); //创建并查集 int totalCost = 0; //答案:总长度 int edgeUsed = 0; //记录已经采用的边,到n-1时停止 // 从最短边开始遍历,尝试不同集合 for(int i=0; i<m; i++){ int u = cost[i][0]-1; //头节点,cost里的节点是从1开始的,而我的并查集是从0开始的 int v = cost[i][1]-1; //尾 int w = cost[i][2]; //代价 if(uf.unite(u, v)){ totalCost += w; edgeUsed ++; if( edgeUsed == n-1){ break; } } } return edgeUsed==n-1 ? totalCost:-1; } };