当前位置: 首页 > news >正文

数据结构 并查集 并查集的操作以及结构

并查集就支持两个操作

  1. 将两个集合合并
  2. 询问两个元素是否在一个集合中

并查集可以在接近o1的时间内,完成这两个操作

别忘了,集合中的元素是不重复的,一般情况下,考察并查集的所有元素也不重复

并查集的结构

并查集是一种树形结构

每个节点存储自己的父节点,例如

f[1]=1,f[2]=1,f[3]=1,f[4]=3,f[5]=3,f[6]=4

并查集初始化

在最初,每个元素都是自己独立一个集合,所以并查集初始化如下

for(int i=1;i<=n;i++)f[i]=i;

并查集查找操作

int find(int x){
    if(f[x]==x)return x;//找到根节点了,因为他指向自己
    return find(f[x]);//不是根节点,继续向上找
}

举例,红色是找过去,绿色是把返回值返回回来

并查集查找优化

并查集既然是只需要支持查找集合,合并集合的操作

那我们可以尽量让这个数变宽,变浅,让子节点在尽量短的路径里,找到根节点

那我们在查找时,就可以更改其他子节点,让所有子节点,直接指向根节点

这样下次再查找时,现有子节点,即可一步(o1)找到根节点

int find(int x){
    if(f[x]==x)return x;
    return f[x]=find(f[x]);//只增加一步操作,将此次查找经过的子节点,其父节点直接指向根节点
}

如下图

并查集合并集合

要记住,f[i]里的i,才是集合里的元素的值,而f[i]的值,是其父节点的值

合并集合,就是把集合A的根节点i,对应的f[i]的值

变成集合B的根节点i的值

代码,将x集合合并到y(x指向y)

void unionset(int x,int y){
    //先用查找函数,找到根节点,然后把根节点f[i]指向的值,从他本身,变为find(y)的集合的根节点的值
    f[find(x)]=find(y);
}

典型例题以及完整代码实现

AcWing - 算法基础课

#include<iostream>
using namespace std;
const int N = 100100;
int f[N];
//查找
int find(int x){
    if(f[x]==x)return x;
    return f[x]=find(f[x]);
}
//合并
void on(int x,int y){
    f[find(x)]=find(y);
    return;
}
int main(){
    int n,m;
    cin>>n>>m;
    //初始化
    for(int i=1;i<=n;i++)f[i]=i;
    while(m--){
        char c;int a,b;
        cin>>c>>a>>b;
        if(c=='M'){
            on(a,b);
        }
        else
        {
            cout<<(find(a)==find(b)?"Yes":"No")<<endl;
        }
    }
    return 0;
}

 连通块中点的数量,经典例题

AcWing - 算法基础课

此题和并查集样题区别是

多了一个查询操作:查询任意一节点所在连通块的数量

连通块查询思路

我们要做的就是,新建一个fs数组,假如根节点是i,则fs[i]存储的就是根节点所在连通块的节点数量

每个连通块的根节点对应一个fs[i]数组,fs[i]的值是该连通块的节点数量

连通块计算思路

一开始,所有fs[i]=1;因为所有节点都是独立的

在每次合并时,例如x所在连通块合并到y所在连通块

我们将x的根节点指向的该连通块所有节点的数量,加在y的根节点指向所有节点数量上

这样便增加fs[i]根节点的值

void on(int x,int y){
    //合并两个连通块(集合)时,先把连通块对应的值计算,因为合并连通块后,对应的fs的值也变了
    //因为该节点的根节点i,f[i]才是该连通块所有节点的数量
    //一定是y累加x的值,因为下面最后一行,是将y的连通块的根节点作为合并后的根节点
    fs[find(y)]+=fs[find(x)]; 
    f[find(x)]=find(y);
}

完成代码实现

#include<iostream>
using namespace std;
const int N = 100010;
#define ll long long
int f[N],fs[N];//fs[i]的值代表i节点作为根节点,共有多少连通块
int find(int x){
    if(f[x]==x)return x;
    return f[x]=find(f[x]);
}
void on(int x,int y){
    //合并两个连通块(集合)时,先把连通块对应的值计算,因为合并连通块后,对应的fs的值也变了
    //因为该节点的根节点i,f[i]才是该连通块所有节点的数量
    //一定是y累加x的值,因为下面最后一行,是将y的连通块的根节点作为合并后的根节点
    fs[find(y)]+=fs[find(x)]; 
    f[find(x)]=find(y);
}
int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        f[i]=i;
        fs[i]=1;//所有节点,一开始节点所在的连通块只有自己本身
    }
    while(m--){
        string c;int a,b;
        cin>>c;
        if(c=="C"){
            cin>>a>>b;
            //同一个节点不用合并,避免多合并,造成根节点对应的数组的值增大
            if (find(a) == find(b)) continue;
            on(a,b);
        }
        else if(c=="Q1"){
            cin>>a>>b;
            cout<<(find(a)==find(b)?"Yes":"No")<<endl;
        }
        else
        {
            cin>>a;
            //输出时,也是该连通块根节点的fs[i]代表连通块数量
            //别忘了先查询,再输出
            cout<<fs[find(a)]<<endl;
        }
    }
    return 0;
}

相关文章:

  • 凸包构造算法—Graham 扫描法
  • 怎么把wps中的word的批注全部删掉
  • ArgoCD 可观测性最佳实践
  • 查看npm安装了哪些全局依赖
  • [electron] electron の 快速尝试
  • 应用分享 | AWG技术突破:操控钻石氮空位色心,开启量子计算新篇章!
  • Window对象的常用属性和方法
  • Git Tag 详解:版本管理与实战指南
  • 【jvm】安全点
  • 顺序表入门
  • Docker学习--容器操作相关命令--docker export 命令
  • 太速科技-330-基于FMC接口的Kintex-7 XC7K325T PCIeX4 3U PXIe接口卡
  • vue-office 支持预览多种文件(docx、excel、pdf、pptx)预览的vue组件库
  • 《C++多线程下单例 “锁钥” 法则》
  • Fast-Poly-2024
  • GodWork 3D 7.24 GodWork AT 7.24天工三维实景三维建模软件
  • 技术与情感交织的一生 (四)
  • 树莓集团引领数字产业生态构建的新力量
  • 汇编学习之《指针寄存器大小端学习》
  • 题解:P8628 [蓝桥杯 2015 国 AC] 穿越雷区
  • 淘客单网站/如何快速推广app
  • 品牌设计全案/seo培训学院官网
  • lnmp怎么做网站/郑州专业网站建设公司
  • 网站开发什么是会话/全网营销方案
  • 没备案的网站怎么做淘客/手机怎么制作网页
  • 在闲鱼可以做网站吗/优化seo方法