这篇博客原理讲得巨详细:算法学习笔记(1) : 并查集 - 知乎
本博客为上述文章的简洁版。
食用顺序:
判断类:1)初始化 2)按要求合并 3)查询是否在同一集合。
计算类:1)初始化 2)合并时计算
目录
路径压缩并查集
初始化
void init(int n)
{
for (int i = 1; i <= n; ++i)
fa[i] = i;
}
查询祖宗节点:将沿途每个节点的父节点设为根节点
int find(int x)
{
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
合并
void merge(int i, int j)
{
fa[find(i)] = find(j);
}
按秩合并并查集(使深度尽量小)
rank[]记录每个根节点对应的树的深度(如果不是根节点,其rank相当于以它作为根节点的子树的深度)。一开始,把所有元素的rank(秩)设为1。合并时比较两个根节点,把rank较小者往较大者上合并。
初始化(按秩合并)
inline void init(int n)
{
for (int i = 1; i <= n; ++i)
{
fa[i] = i;
rank[i] = 1;
}
}
合并(按秩合并)
inline void merge(int i, int j)
{
int x = find(i), y = find(j); //先找到两个根节点
if (rank[x] <= rank[y])
fa[x] = y;
else
fa[y] = x;
if (rank[x] == rank[y] && x != y)
rank[y]++; //如果深度相同且根节点不同,则新的根节点的深度+1
}
最小生成树——并查集版
首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并,直到所有的点都属于同一个集合为止。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int maxm = 1050;
int n, m;
int par[maxn];
struct Edge {
int from, to, dist;
}edges[maxm];
bool cmp(Edge x, Edge y) { return x.dist < y.dist; }
int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); }
int kruskal() {
int ans = 0;
for (int i = 0; i < n; ++i) par[i] = i;
sort(edges, edges + m, cmp);
for (int i = 0; i < m; ++i) {
Edge e = edges[i];
int x = find(e.from);
int y = find(e.to);
if (x != y) { ans += e.dist; par[x] = y; }
}
return ans;
}
int main() {
while (scanf("%d%d", &n, &m) == 2) {
for (int i = 0; i < m; ++i) scanf("%d%d%d", &edges[i].from, &edges[i].to, &edges[i].dist);
cout << kruskal() << endl;
}
return 0;
}