[C++] AcWing 836. 合并集合

文章详细介绍了并查集数据结构的基本原理,包括树状表示、路径压缩技术以及合并和查询集合的操作。通过实例演示了如何使用find函数进行集合查找和合并,展示了路径压缩如何提高查询效率。
摘要由CSDN通过智能技术生成

并查集


  1. 将两个集合合并

  2. 询问两个元素是否在一个集合当中

  3. 基本原理: 每个集合用一颗树来表示。树根的编号就是整个集合的编号。每个结点存储它的父结点,p[x]表示x的父结点。

  4. 问题1: 如何判断树根 if (p[x] == x)

  5. 问题2: 如何求x的集合编号: while (p[x] != x) x = p[x];

  6. 问题3: 如何合并两个集合: px 是 x 的集合编号,py 是 y 的集合编号。p[x] = y。

  7. 路径压缩: find 函数不仅有找祖宗的功能,还把这个查找路径上所有节点直接变成了祖宗节点的孩子

题目


代码


#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int p[N];
int n,m;

//路径压缩: find 函数不仅有找祖宗的功能,还把这个查找路径上所有节点直接变成了祖宗节点的孩子
//路径压缩前: 1->2->3->4->5  需要通过父结点一级一级往上找,最后找到祖宗结点
//路径压缩后: 1->5  2->5  3->5  4->5 不需要一级一级找,直接就能找到祖宗结点
int find(int x)
{
	if(p[x]!=x)
		p[x]=find(p[x]);
	return p[x];
}
int main()
{
	cin>>n>>m;
	//最开始每个数各自在一个集合中,编号是1~n
	//每个数的祖宗结点就是自己
	for(int i=1;i<=n;i++)
		p[i]=i;
	while(m--)
	{
		string op;
		int a,b;
		cin>>op>>a>>b;
		if(op=="M")
			p[find(a)]=p[find(b)];
		else
		{
			if(find(a)==find(b))
				cout<<"Yes"<<endl;
			else
				cout<<"No"<<endl;
		}
	}
	return 0;
}

样例讲解


     int n=4,m=5
     
 idx:   1   2   3   4   
    p[N]:   1   2   3   4  


    M 1 2(将1和2所在的集合合并)
    a=1,b=2
    p[find(a)]=p[find(b)];
    执行过程: 1. find(b): 调用find函数查找 2 的祖宗结点。p[2]=2,2的祖宗结点是2
                    2. p[find(b)]=p[2]=2
                    3. find(a): 调用find函数查找 1 的祖宗结点。p[1]=1,1的祖宗结点是1
                    4. p[find(a)]=p[1]
                    5. 赋值: p[1]=p[2]=2
     idx:    1    2    3    4   
    p[N]:   2    2    3    4   


    M 3 4(将3和4所在的集合合并,即把3的祖宗结点改成4的祖宗结点,或把4的祖宗结点改成3的祖宗结点)
    a=3,b=4
    p[find(a)]=p[find(b)];
    执行过程:  1. find(b): 调用find函数查找 4 的祖宗结点。p[4]=4,4的祖宗结点是4
                      2. p[find(b)]=p[4]=4
                      3. find(a): 调用find函数查找 3 的祖宗结点。p[3]=3,3的祖宗结点是3
                      4. p[find(a)]=p[3]
                      5. 赋值: p[3]=p[4]=4
     idx:    1   2   3   4    
   p[N]:    2   2   4   4    


    Q 1 2(查询1和2是否在同一个集合里,即判断1和2的祖宗结点是否一致)
    a=1,b=2
    if(find(a)==find(b));
    执行过程:  1. find(b): 调用find函数查找 2 的祖宗结点。p[2]=2,2的祖宗结点是2
                      2. p[find(b)]=p[2]=2
                      3. find(a): 调用find函数查找 1 的祖宗结点。
                          1. p[1]!=1,p[1]=find(p[1])
                          2. 现在x=p[1]=2,p[p[1]]=p[2]=2=x=2,return p[x](也就是p[2]=2)
                          3. p[1]=2,1的祖宗结点是2
                      4. 判断: 2 == 2   
                      5. 1和2在同一个集合里


      Q 1 3(查询1和3是否在同一个集合里,即判断1和3的祖宗结点是否一致)
    a=1,b=3
    if(find(a)==find(b));
    执行过程:  1. find(b): 调用find函数查找 3 的祖宗结点。
                         1. p[3]!=3,p[3]=find(p[3])
                         2. 现在x=p[3]=4,p[p[3]]=p[4]=4=x=4,return p[x](也就是p[4]=4)
                         3. p[3]=4,3的祖宗结点是4
                      2. p[find(b)]=p[3]=4
                      3. find(a): 调用find函数查找 1 的祖宗结点。p[1]=2,1的祖宗结点是2
                      4. 判断: 2 != 4
                      5. 1和3不在同一个集合里


      Q 3 4(查询3和4是否在同一个集合里,即判断3和4的祖宗结点是否一致)
    a=3,b=4
    if(find(a)==find(b));
    执行过程:  1. find(b): 调用find函数查找 4 的祖宗结点。p[4]=4,4的祖宗结点是4
                      2. p[find(b)]=p[4]=4
                      3. find(a): 调用find函数查找 3 的祖宗结点。
                          1. p[3]!=3,p[3]=find(p[3])
                          2. 现在x=p[3]=4,p[p[3]]=p[4]=4=x=4,return p[x](也就是p[4]=4)
                          3. p[3]=4,3的祖宗结点是4
                     4. 判断: 4 == 4
                     5. 3和4在同一个集合里


题目链接


https://www.acwing.com/problem/content/838/
              
   

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值