并查集


并查集

并查集的作用

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;
}

文章作者: Cedeat
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Cedeat !
  目录