并查集
并查集的作用
1.进行集合合并。
2.查询两个元素是否处于同一集合。
并查集的原理
用树的形式维护所有的集合
每个集合用一颗树来标志。树的编号就是整个集合的编号。每个节点储存的是他的父节点,p[x]表示x的父节点。
例:
对于一个元素3,若p[3] = 5,则表示3属于编号为5的这个集合当中。
并查集的实现方式(基础操作)
1.如何判断树根
if (p[x] == x)
2.如何求x的集合编号
while (p[x] != x) x = p[x];
每次找节点试都要重新往上找一边,时间复杂度很高,可以进行路线压缩优化。
路线压缩:即找到集合的根节点后,令集合的所有元素指向根结点,以便进行下次查找。
int find (int x) // 返回x的集合编号(祖宗节点) + 路径压缩
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];// 递归方法实现,只有p[x] == x时,即x为祖宗节点时才会返回,令调用过程中所有的p[x]都等于祖宗节点的值。
}
3.如何合并两个集合
p[x] = y;//p[x]为x的集合编号,p[y]为y的集合编号。
p[find(a)] = find(b) || p[find(a)] = p[find(b)] // 因为find(b)即祖宗节点,所以p[find(b)] == find(b),两者含义相同。
完整代码
#include <iostream>
using namespace std;
const int N = 100010;
int p[N];
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n, m;
scanf("%d%d", &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 == 'M') p[find(a)] = find(b);
else
{
if (find(a) == find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}