并查集:一种巧妙的数据结构

并查集:一种巧妙的数据结构

一、并查集简介

并查集(Union-Find)是一种非常经典的数据结构,它主要用于处理一些不相交集合的合并及查询问题。并查集的主要操作有两个:查找和合并。查找操作用于判断一个元素属于哪个集合,合并操作用于将两个不相交的集合合并为一个集合。

二、基本原理

并查集的原理很简单,它维护了一张二维表,表中的每个元素都有一个父节点。当我们需要将两个集合合并时,我们只需要找到这两个集合的根节点,然后将其中一个根节点的父节点设置为另一个根节点即可。这样,两个集合就成为了一个集合。

三、算法流程

  1. 初始化:首先,我们需要选择一个元素作为初始的根节点,然后将其所有的子节点的父节点都指向它自己。

  2. 查找:给定一个元素,我们可以通过递归的方式,从这个元素开始,依次找到它的父节点,直到找到一个指向自己的节点,这个节点就是它的根节点。

  3. 合并:给定两个元素,我们同样可以通过递归的方式,找到它们的根节点,然后将一个根节点的父节点设置为另一个根节点即可。

四、C++代码实现

#include<bits/stdc++.h>  // 引入头文件,包含了常用的标准库函数和数据结构
#define reg register  // 定义宏,表示使用寄存器变量
using namespace std;  // 使用命名空间std,简化代码书写

// 定义read函数,用于读取输入的整数,支持负数输入
inline int read(){
	int x=0,f=1;  // 初始化变量x为0,f为1
	char ch=getchar();  // 读取一个字符作为输入
	while(ch<'0'||ch>'9'){  // 如果输入不是数字,则继续读取
		if(ch=='-')	f=-1;  // 如果输入是负号,则将f设为-1
		ch=getchar();  // 读取下一个字符
	}
	while(ch>='0'&&ch<='9'){  // 如果输入是数字,则转换为整数并计算结果
		x=(x<<1)+(x<<3)+(ch^48);  // 将x左移1位并加上x左移3位再加上ch与48的异或值
		ch=getchar();  // 读取下一个字符
	}
	return x*f;  // 返回计算结果,如果f为-1,则返回负数结果
}

// 定义write函数,用于输出整数x,支持负数输出
void write(int x){
	if(x<0){  // 如果x是负数,则先输出负号
		putchar('-');
		x=-x;  // 取绝对值
	}
	if(x>9)	write(x/10);  // 如果x大于9,则递归调用write函数输出x除以10的结果
	putchar(x%10+'0');  // 输出x除以10的余数,并将其转换为字符输出
	return ;
}

const int MAXN=10004;  // 定义常量MAXN,表示数组fa的最大长度
int fa[MAXN];  // 定义数组fa,用于存储并查集的父节点信息

// 定义get函数,用于获取x的根节点
int get(int x){
	if(x==fa[x])	return x;  // 如果x的父节点就是自身,则说明x是根节点,直接返回x
	return fa[x]=get(fa[x]);  // 否则递归调用get函数,将x的父节点更新为其根节点,并返回根节点
}

// 定义merge函数,用于合并x和y所在的集合
void merge(int x, int y){
	fa[get(x)]=get(y);  // 将x和y所在的集合合并为同一个集合
	return ;
}

// 定义main函数,程序入口
int main(){
	int n=read(),m=read();  // 读取输入的n和m
	for(reg int i=1;i<=n;i++)	fa[i]=i;  // 初始化并查集,每个节点的父节点都是自身
	while(m--){  // 循环执行m次操作
		int op=read();  // 读取操作类型
		if(op==1){  // 如果操作类型为1,则进行合并操作
			int x=read(),y=read();  // 读取要合并的两个节点
			merge(x,y);  // 调用merge函数进行合并
		}
		else{  // 如果操作类型为2,则进行查询操作
			int x=read(),y=read();  // 读取要查询的两个节点
			if(get(x)==get(y))	printf("Y\n");  // 如果两个节点在同一集合中,则输出"Y"
			else	printf("N\n");  // 否则输出"N"
		}
	}
	return 0;  // 返回0,表示程序正常结束
}

五、例题及题解

例题1:给定一个序列,求出所有不相交子序列的数量。

输入:序列的长度n=7,序列为{1,2,3,4,5,6,7}。
输出:所有不相交子序列的数量。

解题思路:我们可以将所有可能的子序列分为两类:包含第一个元素的子序列和不包含第一个元素的子序列。对于每一类子序列,我们都可以在遍历到该元素时,将其并入对应的集合。最后,我们只需要统计不相交集合的数量即可。

代码实现:见上述C++代码实现部分。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值