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

目录

思路

代码如下

一些解释


思路

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

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

由于每个集合都会有其各自属于的连通块,最终又是求某个元素所在连通块中点的数量,所以我们需要开一个数组 记为size 来记录数量 。同样的,我们以根节点为基准,每次更新数量都只需要对根节点对应的数量进行相加即可 ,即size[find(b)]+=size[find(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.先更新数量在进行合并

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

2.find递归

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

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

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

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

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

3.scanf会自动过滤空格

4.关于op

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

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

这样的形式啦。

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

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


写到这里~

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

相关推荐
blasit6 小时前
笔记:Qt C++建立子线程做一个socket TCP常连接通信
c++·qt·tcp/ip
AI软著研究员6 小时前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish7 小时前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱8 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者1 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶
董董灿是个攻城狮1 天前
大模型连载2:初步认识 tokenizer 的过程
算法
地平线开发者1 天前
地平线 VP 接口工程实践(一):hbVPRoiResize 接口功能、使用约束与典型问题总结
算法·自动驾驶
罗西的思考1 天前
AI Agent框架探秘:拆解 OpenHands(10)--- Runtime
人工智能·算法·机器学习
HXhlx1 天前
CART决策树基本原理
算法·机器学习
Wect1 天前
LeetCode 210. 课程表 II 题解:Kahn算法+DFS 双解法精讲
前端·算法·typescript