【数据结构与算法】并查集

文章目录

  • 一、并查集的概念
  • 二、并查集的实现
    • 2.1 find()的实现
    • 2.2 路径压缩算法
    • 2.3 join()的实现
  • 三、并查集的应用
    • 3.1 例题:合并集合
    • 3.2 例题:连通块中点的数量
  • 四、总结

一、并查集的概念

并查集是一个树形结构,所谓的并查,就是当我们有了一个节点,我们就能知道这个节点属于哪个集合

举个例子理解以下:战国时期有很多国家,它们会互相打仗,那么现在有两个互相不认识的人,如何知道对方是敌是友呢?

现在有一种方法:每个国家都有一个大王,我们只要知道自己的上级,上级在知道他的上级,一层层往上,就能找到他的大王是谁,这样就能知道两个人是否是一个国家的了。

二、并查集的实现

首先我们需要一个整型数组p[]它记录的是每个节点的前驱节点。
然后需要find(x)函数,作用是用于查找指定节点x于哪个集合.
函数join(x1,x2) 用于合并两个节点 x1 和 x2

2.1 find()的实现

上面说了我们要确定自己的大王是谁,就可以一层一层的向上询问。
所以我们可以用find来找到节点的根。

int find(int x)					
{
	while(p[x] != x)// 一层层的向上找
		x = p[x];				
	return x;					
}

2.2 路径压缩算法

我们发现如果我们每次都一层一层的向上查找太消耗时间了,所以我们可以在查找的过程把路径压缩。
在这里插入图片描述

int find(int x)
{
    // 路径压缩
    if(p[x] != x) p[x] = find(p[x]);//递归出口:x的上级为 x本身,即 x为根结点  
    return p[x];
}

注意这里第一次查找的时候是没有压缩效果的,第二次及以后才会有效果。

2.3 join()的实现

join就是把两个本来不相关的节点放到一个集合里面,具体实现我们可以借用find函数,对于join(x1,x2),我们只需要让x1的上级变成x2的上级即可。
在这里插入图片描述

void join(int x1, int x2)
{
    p[find(x1)] = find(x2);
}

三、并查集的应用

3.1 例题:合并集合

题目链接

题目描述:

一共有 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≤1e5

输入样例:

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 = 1e5 + 10;
int p[N];

int find(int x)
{
    // 路径压缩
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void join(int x1, int x2)
{
    p[find(x1)] = find(x2);
}

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        p[i] = i;
    }
    while(m--)
    {
        char op;
        int x1, x2;
        cin >> op >> x1 >> x2;
        if(op == 'M')
        {
            join(x1, x2);
        }
        else
        {
            if(find(x1) == find(x2))
            {
                cout << "Yes" << endl;
            }
            else
            {
                cout << "No" << endl;
            }
        }
    }
    return 0;
}

3.2 例题:连通块中点的数量

题目描述:

给定一个包含 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≤1e5

输入样例:

5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5

输出样例:

Yes
2
3

思路分析:
这道题跟上面一道题的不同是这里需要求一个集合有多少个元素,我们的方法是添加一个cnt数组,让每个集合的根节点来记录这个集合里有多少个元素
具体的做法是在join中处理cnt数组,因为查找find并不会改变该集合的元素个数。

void join(int x1, int x2)
{
    cnt[find(x2)] += cnt[find(x1)];
    p[find(x1)] = find(x2);
}

这里要注意顺序不能改变,如果把p[find(x1)] = find(x2);放在前面,find(x1)的根就会发生了变化。所以要先处理cnt数组。

#include <iostream>
#include <string>

using namespace std;

const int N = 1e5 + 10;
int p[N], cnt[N];

int find(int x)
{
    if(p[x] != x)
    {
        p[x] = find(p[x]);
    }
    return p[x];
}

void join(int x1, int x2)
{
    cnt[find(x2)] += cnt[find(x1)];
    p[find(x1)] = find(x2);
}

int main()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        p[i] = i;
        cnt[i] = 1;
    }
    while(m--)
    {
        string op;
        int a, b;
        cin >> op;
        if(op == "C")
        {
            cin >> a >> b;
            if(find(a) != find(b))
            {
                join(a, b);
            }
        }
        else if(op == "Q1")
        {
            cin >> a >> b;
            if(find(a) == find(b))
            {
                cout << "Yes" << endl;
            }
            else
            {
                cout << "No" << endl;
            }
        }
        else
        {
            cin >> a;
            cout << cnt[find(a)] << endl;
        }
    }
    return 0;
}

四、总结

1️⃣ 我们一般用集合中的某个元素来代表这个集合,则该元素称为此集合的代表元。
2️⃣ 对于每一个元素 x,pre[x] 存放 x 在树形结构中的父亲节点(如果 x 是根节点,则令pre[x] = x

一般来说,一个并查集对应两个操作:

  1. 查找函数( Find()函数 )
  2. 合并集合函数( Join()函数 )


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/8070.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

关于神经网络的权重信息和特征图的可视化

目录 1. 介绍 2. 隐藏层特征图的可视化 2.1 AlexNet 网络 2.2 forward 2.3 隐藏层特征图可视化 2.4 测试代码 3. 训练参数的可视化 3.1 从网络里面可视化参数 3.1.1 测试代码 3.1.2 参数的字典信息 3.1.3 参数可视化 3.2 从保存的权重参数文件(.pth)里面可视化参数…

汉诺塔与二进制、满二叉树的千丝万缕

汉诺塔(Tower of Hanoi)源于印度传说中&#xff0c;大梵天创造世界时造了三根金钢石柱子&#xff0c;其中一根柱子自底向上叠着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定&#xff0c;在小圆盘上不能放大圆盘&#xff0c;在三…

数据挖掘(2.3)--数据预处理

目录 三、数据集成和转换 1.数据集成 2.数据冗余性 2.1 皮尔森相关系数 2.2卡方检验 3.数据转换 四、数据的规约和变换 1.数据归约 2数据离散化 三、数据集成和转换 1.数据集成 数据集成是将不同来源的数据整合并一致地存储起来的过程。 不同来源的数据可能有不同…

【ESP32+freeRTOS学习笔记之“ESP32环境下使用freeRTOS的特性分析(2-多核环境中的任务)”】

目录1、ESP32的双核对称多处理SMP概念2、涉及任务task的特殊性2.1 创建任务的特殊函数2.2 xTaskCreatePinnedToCore&#xff08;&#xff09;函数的解释3、任务的删除4、总结1、ESP32的双核对称多处理SMP概念 最初的FreeRTOS&#xff08;以下简称Vanilla FreeRTOS&#xff09;…

线性表——顺序表

文章目录一&#xff1a;线性表二&#xff1a;顺序表1&#xff1a;概念与结构1&#xff1a;静态顺序表2&#xff1a;动态顺序表2&#xff1a;动态顺序表的代码实现1&#xff1a;结构2&#xff1a;接口实现1&#xff1a;初始化2&#xff1a;释放内存3&#xff1a;检查容量4&#…

Linux下最小化安装CentOS-7.6(保姆级)

文章目录安装包开始安装一、 新建一个虚拟机二、配置安装CentOS7.6二、开始安装CentOS三、配置CentOS并下载基本信息安装包 链接&#xff1a;https://pan.baidu.com/s/1DodB-kDy1yiNQ7B5IxwYyg 提取码&#xff1a;p19i 开始安装 一、 新建一个虚拟机 1、 打开VMWare&#x…

刷题笔记【5】| 快速刷完67道剑指offer(Java版)

本文已收录于专栏&#x1f33b;《刷题笔记》文章目录前言&#x1f3a8; 1、合并两个有序链表题目描述思路一&#xff08;递归&#xff09;思路二&#xff08;双指针&#xff09;&#x1f3a8; 2、树的子结构题目描述思路一&#xff08;递归&#xff09;&#x1f3a8; 3、二叉树…

Redis分布式锁系列

1.压力测试出的内存泄漏及解决&#xff08;可跳过&#xff09; 使用jmeter对查询产品分类列表接口进行压力测试&#xff0c;出现了堆外内存溢出异常。 我们设置的虚拟机堆内存100m&#xff0c;并不是堆外内存100m 产生堆外内存溢出&#xff1a;OutOfDirectMemoryError 原因是…

某大厂面试题:说一说Java、Spring、Dubbo三者SPI机制的原理和区别

大家好&#xff0c;我是三友~~ 今天来跟大家聊一聊Java、Spring、Dubbo三者SPI机制的原理和区别。 其实我之前写过一篇类似的文章&#xff0c;但是这篇文章主要是剖析dubbo的SPI机制的源码&#xff0c;中间只是简单地介绍了一下Java、Spring的SPI机制&#xff0c;并没有进行深…

SQL——数据查询DQL

基本语句、时间查询&#xff08;当天、本周&#xff0c;本月&#xff0c;上一个月&#xff0c;近半年的数据&#xff09;。 目录 1 查询语句基本结构 2 where 子句 3 条件关系运算符 4 条件逻辑运算符 5 like 子句 6 计算列 7 as 字段取别名 8 distinct 清除重复行 9 …

linux mysql

安装 下载包 wget https://cdn.mysql.com/archives/mysql-8.0/mysql-8.0.31-1.el8.x86_64.rpm-bundle.tar解压 tar -zxvf mysql-8.0.31-1.el8.x86_64.rpm-bundle.tar -C /usr/local/mysql安装openssl-devel插件 yum install openssl-devel安装rpm包 使用rpm -ivh安装图中r…

【Unity项目实战】从零手戳一个背包系统

首先我们下载我们的人物和背景资源,因为主要是背包系统,所以人物的移动和场景的搭建这里我们就不多讲了,我这里直接提供基础项目源码给大家去使用就行 基础项目下载地址: 链接: https://pan.baidu.com/s/1o7_RW_QQ1rrAbDzT69ApRw 提取码: 8s95 顺带说一下,这里用到了uni…

AttributeError: module transformers has no attribute LLaMATokenizer解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

AES加密

来源&#xff1a;链接: b站up主可厉害的土豆 &#xff08;据评论区说图片中有计算错误&#xff0c;但是过程是对的。只是了解过程问题不大&#xff0c;专门研究这一块的话自己可以看视频算一下&#xff09; 准备 首先&#xff0c;明文是固定长度 16字节 128位。 密钥长度可以…

TCP协议一

TCP数据报格式 TCP通信时序 下图是一次TCP通讯的时序图。TCP连接建立断开。包含大家熟知的三次握手和四次握手。 在这个例子中&#xff0c;首先客户端主动发起连接、发送请求&#xff0c;然后服务器端响应请求&#xff0c;然后客户端主动关闭连接。两条竖线表示通讯的两端&…

houjie-cpp面向对象

houjie 面向对象 面向对象&#xff08;上&#xff09; const 在一个函数后面放const&#xff0c;这个只能修饰成员函数&#xff0c;告诉编译器这个成员函数不会改数据 const还是属于函数签名的一部分。 引用计数&#xff1a;涉及到共享的东东&#xff0c;然后当某个修改的时候&…

Mysql的学习与巩固:一条SQL查询语句是如何执行的?

前提 我们经常说&#xff0c;看一个事儿千万不要直接陷入细节里&#xff0c;你应该先鸟瞰其全貌&#xff0c;这样能够帮助你从高维度理解问题。同样&#xff0c;对于MySQL的学习也是这样。平时我们使用数据库&#xff0c;看到的通常都是一个整体。比如&#xff0c;你有个最简单…

【Paper】2019_Resilient Consensus Through Asynchronous Event-based Communication

Wang Y, Ishii H. Resilient consensus through asynchronous event-based communication[C]//2019 American Control Conference (ACC). IEEE, 2019: 1842-1847. 文章目录I. INTRODUCTIONII. EVENT-BASED RESILIENT CONSENSUS PROBLEMA. Preliminaries on graphsB. Event-base…

基于Java+ SpringBoot+Vue 的网上图书商城管理系统(毕业设计,附源码,教程)

您好&#xff0c;我是程序员徐师兄&#xff0c;今天为大家带来的是 基于Java SpringBootVue 的网上图书商城管理系统&#xff08;毕业设计&#xff0c;附源码&#xff0c;教程&#xff09;。 &#x1f601; 1.Java 毕业设计专栏&#xff0c;毕业季咱们不慌忙&#xff0c;几百款…

电脑桌面图标间距突然变大怎么恢复

1. WindowsR打开 > 输入regedit 按住WindowsR打开运行&#xff0c;输入regedit并点击确定。 2. 双击Control Panel 双击展开HKEY_CURRENT_USER&#xff0c;双击展开Control Panel&#xff0c;双击展开Desktop。 3. 更改间距 点击打开WindowMetrics&#xff0c; 双击打开…