【第十四课】并查集(acwing-837连通块中点的数量 / c++代码 / 思路详解)

目录

思路

代码如下

一些解释


思路

由于这道题是在并查集这个知识点下面,所以自然我们直接将无向图及之间连线的表示模型化为我们并查集的模板(或许其实也并不难想到?),要解释一下的话就是:我们将无向图中的每个顶点当作一个集合,顶点之间的连线当作两个集合的合并,而连通块就是有连线的边和顶点。

对于前两个操作和之前是一样的。主要是操Q2操作,要求连通块中点的数量 ,其实转化成我们之前的语言就是我们合并之后这个集合中一共有多少个元素,我刚开始想的是既然要求一共有多少个元素,应该只能从集合合并这一步下手,通过每次合并时的元素的相加实现。但其实虽然想到了这里,但并没有想到应该怎样来实现🥀

由于每个集合都会有其各自属于的连通块,最终又是求某个元素所在连通块中点的数量,所以我们需要开一个数组 记为size 来记录数量 。同样的,我们以根节点为基准,每次更新数量都只需要对根节点对应的数量进行相加即可 ,即sizefind(b)+=sizefind(a); 在自身的基础上加上将要合并的另一个集合的元素个数,之后更改该集合所以元素的父节点,完成合并。

另外题目中三个操作都强调a b可能相等这个条件,主要是我们并查集的模板设计的就是不含有重复元素,但是在以并查集为模型的题目中 ,就比如这道题在无向图的框架之下,由于无向图中会存在环,也就是元素重复的情况,所以这里要含有一个if特判。

代码如下

cpp 复制代码
#include<iostream>
using namespace std;
const int N=1e5+10;
int n,m,p[N],size[N];
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];//不要写else
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
        size[i]=1;//初始状态都只有其自身这一个节点
    }
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s",op);
        if(op[0]=='C')
        {
            scanf("%d%d",&a,&b);//scanf会自动过滤空格
            if(p[a]==p[b])continue;
            size[find(b)]+=size[find(a)];//这里两个集合并没有合并,还是两棵树
            p[find(a)]=find(b);//这里更新了两棵树中所有元素的根节点
        }
        else if(op[1]=='1')//注意这里的判断条件
        {
            scanf("%d%d",&a,&b);
            if(p[a]==p[b]) puts("Yes");
            else puts("No");
        }
        else {
            scanf("%d",&a);
            printf("%d",size[find(a)]);
        }
    }
    return 0;
}

一些解释

1.先更新数量在进行合并

注意要先更新数量在进行合并 ,因为我们合并之后两个集合就变成一个集合,拥有同一个父节点了,如果先合并,在执行更新数量的代码时,会导致统计结果是翻倍的,因为合并集合之后 ,变成了 sizefind(b)+=sizefind(b); 。导致数量更新错误。

2.find递归

上次写的时候没有解释出来,这次补上,可能会容易写错(┭┮﹏┭┮是我写错了)

cpp 复制代码
int find(int x)
{
    if(p[x]!=x)p[x]=find(p[x]);
    return p[x];//不要写else
}

**无论if条件是否满足,我们都需要返回px。**如果我们把return px;放在else语句中,那么在if条件满足的情况下,函数就没有返回值了。

有一个想法上的误区 :我把return放在else里是想着总会递归到不满足if条件的时候然后再执行else语句就会执行了return,然而这样乍一想好像没问题,但是这样的话会导致我们最终这个函数没有返回值 ,想一下如果我们执行了多层递归,由于只有根节点的px=x,多层递归函数在遇到根节点之后会返回一个值,结束最内层的递归函数 ,注意它只会返回最内层递归调用的结果。对于外层的递归调用,由于return语句在else中,它们不会返回任何值。从而导致了这种写法 会导致 函数没有返回值的客观结论。

关于这个集合合并如果难理解的话,建议画图模拟一下合并的过程。

3.scanf会自动过滤空格

4.关于op

如果将char op2替换为stringop,那么在判断执行操作的类型时,可以直接使用字符串比较。也就是如果创建了string类型,就if语句就可以直接写成

cpp 复制代码
if(op == "Q1")

这样的形式啦。

我们之前说过,把 op 定义为char数组形式的时候,是不可以这样写的,要使用strcmp函数。

这是因为在C++中,char数组实际上是一个指针 ,指向数组的第一个元素。当写op == "Q1"时,实际上是在比较两个指针,而不是比较两个字符串的内容。


写到这里~

有问题欢迎指出!一起加油!!

相关推荐
通信小呆呆42 分钟前
电子战抗干扰技术:当“不被干扰”成为一种系统
算法·通信·mimo·isac
老歌老听老掉牙44 分钟前
案例 7:二维 Rosenbrock 函数最小值(香蕉函数)
c++·pso
笑容掩饰我的失落1 小时前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
Severus_black1 小时前
【C++初阶】Stack、Queue算法题大练兵!
c语言·c++·算法·stl
2601_967979124 小时前
机器学习必修课:经典算法与Python实战
python·算法·机器学习·实战·课程
程序猿打工仔4 小时前
计算机基础(C语言)考编、专技岗
c语言·开发语言·算法
通信小呆呆5 小时前
电子战干扰技术:从“蛮力压制”到“精准欺骗”,再到“认知博弈”
算法·通信·isac
老师我太想进步了20265 小时前
【数据结构-6】算法美学之单链表(1)
数据结构·算法
xiaoye-duck6 小时前
《C++ 项目:手写大模型接入 SDK》DataManager数据管理模块:SQLite持久化存储
开发语言·数据库·c++·sqlite
希望我万事顺遂6 小时前
力扣hot100-滑动窗口最大值
数据结构·算法·leetcode