[数据结构与算法-02]并查集
并查集
特点
并查集被很多OIer认为是最简洁而优雅的数据结构之一,主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
- 合并(Union):把两个不相交的集合合并为一个集合。
- 查询(Find):查询两个元素是否在同一个集合中。
思路
-
存储/初始化:
-
每个集合用一个代表元素(称作
根)表示- 这个代表元素必须满足其老大为自身
-
用一个数组来记录每个元素的老大(不一定是所在集合的代表元素)比如a的老大是b,则表示a属于b所在的集合,而这个集合的代表元素可能为c
-
每个元素各自为阵,根为自身,每个元素独立构成一个集合
-
-
查询(Find):找到元素所在集合的根,递归实现
- 判断这个元素的老大是否是自身:
- 若是,返回
- 若否,找老大的根
- 判断这个元素的老大是否是自身:
-
合并(Union):让一个元素的根做另一个元素的根的老大
-
路径压缩:由于查询时要经过所有的上级,可顺便让这个元素的老大直接变为根
-
按秩合并:为避免树的深度加深,把简单的树(子孙节点少)往复杂的树上合并(子孙节点多),这里用树的深度近似
- 用一个数组rank[]记录每个节点所对应的树的深度(子节点对应子树的深度)
- 一开始所有树的rank均为1
- 合并时将rank小者向rank大者合并
- rank相同,新的根节点rank要+1
- 按rank合并会带来额外的空间开销
-
路径压缩和按秩合并如果一起使用,时间复杂度接近O(n) ,但是很可能会破坏rank的准确性。
代码实现
初始化
inline void init(){
for (int element = 0; element < n; element++)
fa[element] = element;
}
查询(路径压缩)
int find(int element){
if (fa[element] == element)return element;
else{
fa[element] = find(fa[element]);
return father[element];
}
}
合并(按rank合并)
void union(int eleOne, int eleTwo){
int eleOneFa = find(eleOne);
int eleTwoFa = find(eleTwo);
if (eleOneFa == eleTwoFa);
else{
if (rank[eleOneFa] < rank[eleTwoFa])
fa[eleOneFa] = eleTwoFa;
else if (rank[eleOneFa] > rank[eleTwoFa])
fa[eleTwoFa] = eleTwoFa;
else{
fa[eleOneFa] = eleTwoFa;
rank[eleTwoFa]++;
}
}
}
P3367 【模板】并查集
题目描述
如题,现在有一个并查集,你需要完成合并和查询操作。
输入格式
第一行包含两个整数 N,M 表示共有 N 个元素和 M 个操作。
接下来 MM 行,每行包含三个整数 Z_i,X_i,Y_iZ**i,X**i,Y**i 。
当 Z_i=1Z**i=1 时,将 X_iX**i 与 Y_iY**i 所在的集合合并。
当 Z_i=2Z**i=2 时,输出 X_iX**i 与 Y_iY**i 是否在同一集合内,是的输出
Y;否则输出N。输出格式
对于每一个 Z_i=2Z**i=2 的操作,都有一行输出,每行包含一个大写字母,为
Y或者N。输入输出样例
输入 #1复制
4 7 2 1 2 1 1 2 2 1 2 1 3 4 2 1 4 1 2 3 2 1 4输出 #1复制
N Y N Y说明/提示
对于 30%30% 的数据,N \le 10N≤10,M \le 20M≤20 。
对于 70%70% 的数据,N \le 100N≤100,M \le 10^3M≤103 。
对于 100%100% 的数据,1\le N \le 10^41≤N≤104,1\le M \le 2\times 10^51≤M≤2×105 。
完整解答
#include <cstdio>
#define MAXN 10000
int X, Y, M, N, fa[MAXN + 10];
short op;
void init(int n){
for (int i = 0; i < n; i++) fa[i] = i;
}
int find(int element){
if (fa[element] == element)return element;
else{
fa[element] = find(fa[element]);
return fa[element];
}
}
int Union(int eleOne, int eleTwo){
fa[find(eleOne)] = find(eleTwo);
}
int main(){
scanf("%d%d", &N, &M);
init(N);
for (int i = 0; i < M; i++){
scanf("%hd%d%d", &op, &X, &Y);
if (op == 1){
Union(X,Y);
} else if (op == 2){
if (find(X) == find(Y)){
printf("Y\n");
}else{
printf("N\n");
}
}
}
}

浙公网安备 33010602011771号