并查集:一种巧妙的数据结构
并查集:一种巧妙的数据结构
一、并查集简介
并查集(Union-Find)是一种非常经典的数据结构,它主要用于处理一些不相交集合的合并及查询问题。并查集的主要操作有两个:查找和合并。查找操作用于判断一个元素属于哪个集合,合并操作用于将两个不相交的集合合并为一个集合。
二、基本原理
并查集的原理很简单,它维护了一张二维表,表中的每个元素都有一个父节点。当我们需要将两个集合合并时,我们只需要找到这两个集合的根节点,然后将其中一个根节点的父节点设置为另一个根节点即可。这样,两个集合就成为了一个集合。
三、算法流程
-
初始化:首先,我们需要选择一个元素作为初始的根节点,然后将其所有的子节点的父节点都指向它自己。
-
查找:给定一个元素,我们可以通过递归的方式,从这个元素开始,依次找到它的父节点,直到找到一个指向自己的节点,这个节点就是它的根节点。
-
合并:给定两个元素,我们同样可以通过递归的方式,找到它们的根节点,然后将一个根节点的父节点设置为另一个根节点即可。
四、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++代码实现部分。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)