【第十四课】并查集(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"时,实际上是在比较两个指针,而不是比较两个字符串的内容。


写到这里~

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

相关推荐
怀澈12210 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming198733 分钟前
STL关联式容器之set
开发语言·c++
带多刺的玫瑰37 分钟前
Leecode刷题C语言之统计不是特殊数字的数字数量
java·c语言·算法
爱敲代码的憨仔38 分钟前
《线性代数的本质》
线性代数·算法·决策树
威桑44 分钟前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝1 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp