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

目录

思路 

代码如下

一些解释 


思路 

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

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

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

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

代码如下

#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递归

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

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]替换为string op,那么在判断执行操作的类型时,可以直接使用字符串比较。也就是如果创建了string类型,就if语句就可以直接写成  

if(op == "Q1")

这样的形式啦。

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

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


写到这里~

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

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值