思路:
1. 初始化:
假如有编号为1,2,3,.., n的n个元素,我们用一个数组p来存储每个元素的父节点。一开始,我们先将它们的父节点设为自己
int p[N];
for(int i = 1;i <= n;i ++)p[i] = i;
2. 查询
找到i的祖先直接返回,未进行路径压缩
int find(int i){if(p[i] == i)return i;else return find(p[i]);
}
3.合并
void union(int i,int j){p[find(i)] = find(j); //i的祖先指向i的祖先。
}
路径压缩:
int find(int x){ //返回x的祖宗节点 + 路径压缩if(p[x] != x) p[x] = find(p[x]);return p[x];
}
例题:836. 合并集合 - AcWing题库
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
M a b
,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;Q a b
,询问编号为 a 和 b 的两个数是否在同一个集合中;输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为
M a b
或Q a b
中的一种。输出格式
对于每个询问指令
Q a b
,都要输出一个结果,如果 a 和 b 在同一集合内,则输出Yes
,否则输出No
。每个结果占一行。
数据范围
1≤n,m≤10^5
输入样例:
4 5 M 1 2 M 3 4 Q 1 2 Q 1 3 Q 3 4
输出样例:
Yes No Yes
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<string>
#include<queue>
#include<cstring>
#include<sstream>
#include<string.h>
#include<vector>const int N = 1e5 + 10;
using namespace std;
typedef pair<int ,int> PII;
int n,m;
int p[N];
int find(int x){ //返回x的祖宗节点 + 路径压缩if(p[x] != x) p[x] = find(p[x]);return p[x];
}
//find函数找祖先
int main(){cin >> n >> m;for(int i = 1;i <= n;i ++)p[i] = i;while(m --){ char op[2];int a,b;scanf("%s%d%d",op,&a,&b);if(op[0] == 'M') p[find(a)] = find(b);//a->belse{if(find(a) == find(b)) cout << "Yes" << endl;else cout << "No" << endl;}}return 0;
}