算法 -- 02 -- 并查集

117 阅读1分钟

朴素并查集

836. 合并集合 - AcWing题库

思想:可以将每个集合想像成一颗树,根节点代表这棵树所属的集合

根节点 p[x] = x;

子节点 p[son] = x;

每次合并两个集合就是将一棵树的合并成另一棵树的子树。

查询就是查询根节点是否相同

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int p[N];
​
//路径压缩,通过递归和回溯,将集合中每个节点的值都赋值为根节点
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
​
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);
        }
        else
        {
            if(find(a) == find(b)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}

维护size的并查集

837. 连通块中点的数量 - AcWing题库

与朴素并查集不同的地方在于,合并时需要判断不在一个集合才能进行合并操作

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int p[N];
int _size[N];
​
//路径压缩
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}
​
int main()
{
    cin>>n>>m;
    for(int i = 1; i <= n; i++) 
    {
        p[i] = i;
        _size[i] = 1;
    }
    
    while (m -- ){
        char op[5];
        int a, b;
        scanf("%s", op);
        if(op[0] == 'C')
        {
            cin>>a>>b;
            if(find(a) == find(b)) continue;
            _size[find(b)] += _size[find(a)];
            p[find(a)] = find(b);
        }
        if(op[0] == 'Q')
        {
            if(op[1] == '1')
            {
                cin>>a>>b;
                if(find(a) == find(b)) cout<<"Yes"<<endl;
                else cout<<"No"<<endl;
            }
            if(op[1] == '2')
            {
                cin>>a;
                cout<<_size[find(a)]<<endl;
            }
        }
    }
    return 0;
}

\