并查集
是一种数据结构,常用于解决元素分组的问题
管理一系列不相交的集合,支持合并和查询两种操作
主要思想:用集合的一个元素代表集合,即祖宗代表家族
若是了解一些图论基础,可以发现并查集类似于一棵树,根节点就是祖宗,子节点就是集合的其他元素
基础代码
int f[N];
void init(int n) {
for (int i = 1; i <= n; ++i) f[i] = i;
}
int find(int x) {
if (f[x] == x)
return x;
else
return find(f[x]);
}
void merge(int x, int y) {
if (find(x) != find(y))
f[find(x)] = f(y);
}
压缩路径
事实上,基础查询的合并效率是很低的,因为存在大量重复查询
这就要使用路径压缩,原理是在查询过程中,把沿途经过的每个节点的父节点都设为根节点
int f[N];
void init(int n) {
for (int i = 1; i <= n; ++i) f[i] = i;
}
int find(int x) {
return x == f[x] ? x : (f[x] = find(f[x]));
}
void merge(int x, int y) {
if (find(x) != find(y))
f[find(x)] = f(y);
}
注意:赋值运算符(=)的优先级没有三元运算符(?:)高,这里要加括号
按秩合并
因为并查集可以画成一棵树,也就意味着不同的集合存在深度差异
那么在合并时,是把深度小的合并到深度大的,还是相反?
当然是前者。从查询效率考虑,深度小的树的平均查询时间也小
这样做的方法也很简单,用一个rank数组记录每个根节点对应树的深度,初始化为1,合并时把rank值较小者往较大者上合并
int f[N], rank[N];
void init(int n) {
for (int i = 1; i <= n; ++i) {
f[i] = i;
rank[i] = 1;
}
}
int find(int x) {
return x == f[x] ? x : (f[x] = find(f[x]));
}
void merge(int x, int y) {
int fx = find(x), fy = find(y);
if (rank[fx] <= rank[fy])
f[fx] = fy;
else
f[fy] = fx;
if (rank[fx] == rank[fy] && fx != fy)
rank[fy]++;
}

浙公网安备 33010602011771号