数据结构——并查集

并查集的作用:

并查集,顾名思义就是 能并又能查的集合,它可以在近乎O(1)的时间内完成以下两个操作

1、将两个集合合并

2、询问两个元素是否在一个集合中


 基本原理:

用“树”的形式来维护每一个集合,树根的编号就是整个集合的编号,每个结点存储它的父结点(如:p[x]表示x的父结点)


问题1:如何判断树根?    A:if(p[x]==x),当前x就是树根了,因为除了树根其他的结点存储的都是它的父结点。

问题2:如何求x的集合编号?     A:while(p[x]!=x) x=p[x]; 

问题3:如何合并两个集合?       A:假设px是x的集合编号,py是y的集合编号,则p[px]=py,即将x集合的树根连接到y集合的树根下作为其子结点。

 

当前缺点及其优化?

缺点:这样子第二个操作,即求x的集合编号还是太慢了,需要while循环树的高度,需要优化。

优化:路径压缩!在第一遍求x的根结点时,一旦找到其根结点,将其路径上的所有结点的父结点都变成这个根结点,即树的高度变小了。

左图为路径压缩的简单图示,初始时树用黑色连线连接,从叶子结点找到根结点后,将路径上的所有非根结点的父结点都变成根结点,用红色连线表示。


 代码实现:

题目参考acwing模板题:

836. 合并集合 - AcWing题库

代码参考:

复制代码
#include<iostream>
using namespace std;
const int N=100010;
int p[N];//用双亲表示法来存储树,即p[x]存储x的父节点。初始化时,每个元素自成一个单元素子集合
int find(int x){
    if(p[x]!=x)p[x]=find(p[x]);//巧妙的find函数核心,递归的同时实现了路径压缩,递归第一层找父结点,第二层找父结点......一直递归下去,找到根结点之后会原路返回,将所有路径上的结点的父结点都变成根结点
    //递归需要加以限制,不进行限制的递归就会爆栈
    return p[x];
    //如果是下面这种写法,则没有进行状态压缩,只找了根结点,会超时
    // while(p[x]!=x)x=p[x];
    // return x;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        p[i]=i;
    }
    while(m--){
        char op;
        cin>>op;
        int a,b;
        cin>>a>>b;
        if(op=='M'){
            p[find(a)]=find(b);
        }
        else{
            if(find(a)==find(b))cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}
复制代码

 

posted @   blazerrr  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示