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

数据结构 并查集

作用

快速的处理以下问题:【近乎O(1)的时间完成】

1.将两个集合合并

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

用树的形式维护集合

基本原理

每一个集合用一棵树表示

每一个集合的编号就是根结点的编号,对于每一个结点,都存储其父结点,p[x]表示x的父结点,即p[x]=a表示编号为x的结点的父结点的编号为a

求某个点属于哪个集合时,就先找其父结点,如果其父结点不是根结点,那么就继续找其父结点的父结点,直到找到其根结点为止

问题1

如何判断树根:if(p[x]==x)

问题2

如何求x的集合编号:while(p[x]!=x) x=p[x];【只要x不是树根,就一直往上走,直到找到树根为止】

该步骤时间复杂度仍然很高,需要进行以下优化:

路径压缩:一旦找到根结点,就会把整个路径上所有点都指向根结点。【基本O(1)】

安秩合并【一般不用】

问题3

如何合并两个集合:px是x集合的集合编号【即x集合的根节点的编号是px】,py是y集合的集合编号【即y集合的根节点的编号是py】 p[px]=py或p[py]=px

代码实现

int find(int x) {//返回x所在集合的编号+路径压缩

if (p[x] != x)

p[x] = find(p[x]);

return p[x];

}

例题——合并集合

一共有n个数,编号是1~n,最开始每个数各自在一个集合中。

现在要进行m个操作,操作共有两种:

“M a b”,将编号为a和b的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;

“Q a b”,询问编号为a和b的两个数是否在同一个集合中;

输入格式

第一行输入整数n和m。

接下来m行,每行包含一个操作指令,指令为“M a b”或“Q a b”中的一种。

输出格式

对于每个询问指令”Q a b”,都要输出一个结果,如果a和b在同一集合内,则输出“Yes”,否则输出“No”。

每个结果占一行。

数据范围

1≤n,m≤105

输入样例

4 5

M 1 2

M 3 4

Q 1 2

Q 1 3

Q 3 4

输出样例

Yes

No

Yes

代码

#include<iostream>

using namespace std;

const int N = 100010;

int p[N];//存储父结点

int n, m;

int find(int x) {//返回x所在集合的编号+路径压缩

if (p[x] != x)

p[x] = find(p[x]);

return p[x];

}

int main() {

scanf("%d%d", &n, &m);

for (int i = 1;i <= n;i++)

p[i] = i;

while (m--) {

char op[2];//因为在使用scanf进行读取时,若读取单个字符会读取到空格、回车等其他字符,使用scanf读取字符串时可忽略空格、回车等其他字符

int a, b;

scanf("%s%d%d", op, &a, &b);

if (op[0] == 'M')

p[find(a)] = find(b);//合并

//find(a)返回a的祖宗结点,find(b)返回b的祖宗结点,让a的祖宗结点的父结点等于b的祖宗结点

else {

if (find(a) == find(b))

puts("Yes");

else

puts("No");

}

}

return 0;

}

例题——连通块中点的数量

给定一个包含n个点(编号为1~n)的无向图,初始时图中没有边。

现在要进行m个操作,操作共有三种:

“C a b”,在点a和点b之间连一条边,a和b可能相等;

“Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;

“Q2 a”,询问点a所在连通块中点的数量;

输入格式

第一行输入整数n和m。

接下来m行,每行包含一个操作指令,指令为“C a b”,“Q1 a b”或“Q2 a”中的一种。

输出格式

对于每个询问指令”Q1 a b”,如果a和b在同一个连通块中,则输出“Yes”,否则输出“No”。

对于每个询问指令“Q2 a”,输出一个整数表示点a所在连通块中点的数量

每个结果占一行。

数据范围

1≤n,m≤10^5

输入样例

5 5

C 1 2

Q1 1 2

Q2 1

C 2 5

Q2 5

输出样例

Yes

2

3

代码

#include<iostream>

using namespace std;

const int N = 100010;

int p[N], size1[N];//存储父结点

int n, m;

int find(int x) {//返回x所在集合的编号+路径压缩

if (p[x] != x)

p[x] = find(p[x]);

return p[x];

}

int main() {

scanf("%d%d", &n, &m);

for (int i = 1;i <= n;i++) {

p[i] = i;

size1[i] = 1;

}

while (m--) {

char op[5];

int a, b;

scanf("%s", op);

if (op[0] == 'C') {

scanf("%d%d", &a, &b);

if (find(a) == find(b))

continue;

//特判:如果两个数已经在一个集合,就不用再次合并,不然会使得元素中的元素个数翻倍

size1[find(b)] += size1[find(a)];//b中元素数量为其本身加上a中元素数量

p[find(a)] = find(b);//合并,将a合并到b中

}

else if (op[1] == '1') {

scanf("%d%d", &a, &b);

if (find(a) == find(b))

puts("Yes");

else

puts("No");

}

else {

scanf("%d", &a);

printf("%d\n", size1[find(a)]);

}

}

return 0;

}


http://www.kler.cn/news/136308.html

相关文章:

  • 强大!Spring Boot 3.3 集成 PDFBox 轻松实现电子签章功能!
  • Vue3 + TypeScript 实现 iframe 嵌入与通信的完整指南以及全屏弹窗方案
  • 线性表之双向链表
  • 如何加密电脑磁盘?电脑本地磁盘加密方法介绍
  • 论文解析八: GAN:Generative Adversarial Nets(生成对抗网络)
  • 51单片机快速入门之 AD(模数) DA(数模) 转换 2024/10/25
  • 11.16~11.19绘制图表,导入EXCEL中数据,进行拟合
  • 一道简单的积分题目
  • 【机器学习】036_权重衰退
  • B Label, BL Label 指令
  • 基于安卓android微信小程序美容理发店预约系统app
  • 工具及方法 - 多邻国: Duolingo
  • LeetCode第七题整数反转
  • 什么是一阶逻辑?
  • 数据结构【DS】图的遍历
  • 2311rust,到66版本更新
  • 简单模拟 Spring 创建的动态代理类(解释一种@Transactional事务失效的场景)
  • 使ros1和ros2的bag一直互通
  • Go解析soap数据和修改其中数据
  • MR素数测试及 pycryptodome库下 已知MR伪素数以及强伪证 生成指定伪随机数生成器绕过素性检测
  • 网络工程师-HCIA网课视频学习
  • Apache Airflow (十二) :PythonOperator
  • 【Linux】【开发】使用sed命令遇到的乱码问题
  • 内置函数和消息传递API
  • 类与对象(上篇)
  • WinForms C# 导入和导出 CSV 文件 Spread.NET