首页 > 其他分享 >并查集:一种巧妙的数据结构

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

时间:2023-08-18 21:22:38浏览次数:48  
标签:int 查集 巧妙 fa 序列 集合 数据结构 节点

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

一、并查集简介

并查集(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++代码实现部分。

标签:int,查集,巧妙,fa,序列,集合,数据结构,节点
From: https://www.cnblogs.com/Nebulary/p/17641627.html

相关文章

  • JavaScript中常见的数据结构和算法及其应用场景简介
    在JavaScript编程中,数据结构和算法是必不可少的组成部分。本文将介绍JavaScript中常见的数据结构和算法以及它们的应用场景。数据结构数组数组是JavaScript中最常见的数据结构之一。它是一种有序的集合,可以存储任意类型的数据。由于数组支持快速随机访问,因此它非常适合用于存......
  • 什么是数据结构
    一、数据结构的起源1968年,美国高德纳教授,《计算机程序设计艺术》第一卷《基本算法》提出,开创了数据结构与算法的先河数据结构是一门研究数据之间关系、操作的学科,而非计算数据方法数据结构+算法=程序揭露了程序的本质,沃思凭借这个观点获得了图灵奖二、数据结构中的基本概......
  • 【数据结构】选择排序 简单选择+堆排序
    选择排序的基本思想是每次从待排序的序列中选出最小值(或者最大值)依次放在已排序序列中,直到待排序序列为空,此时序列已完全有序。选择排序的选择只需要进行n-1趟,因为当剩余元素数量为1时无需再选择,直接放在排序序列的末尾即可。在这里学简单选择排序和堆排序两种算法,简单选择考的......
  • 「Note」数据结构方向 - 可持久化数据结构
    1.可持久化线段树1.1.介绍可持久化线段树一般用于解决区间第\(k\)小值的询问。首先考虑简化过的问题,区间\(\left[1,r\right]\)的第\(k\)小值。考虑用权值线段树(离散化或动态开点)来求\(k\)小值,接下来只需要解决区间的问题。可持久化线段树核心思想:每次插入值时保留......
  • 数据结构--选择排序
    数据结构--选择排序简单选择排序在待排序的数据中选出最大的(小)的元素放在其最终位置.简单选择排序的演示简单选择排序算法简单选择排序算法分析排序方法的比较.堆排序......
  • 数据结构--交换排序
    数据结构--交换排序基本思想:两两比较,如果发生逆序则交换,直到所有记录都排好序为止.冒泡排序每趟不断将记录两两比较,并且按照"前小后大"规则交换.冒泡排序的过程演示n个记录,需要比较n-1趟.第m躺需要比较n-m次冒泡排序算法描述还可以继续优化:某一趟比较时不出现......
  • 考研数据结构——每日一题[快速排序]
    785.快速排序给定你一个长度为n的整数数列。请你使用快速排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。输入格式输入共两行,第一行包含整数n。第二行包含n个整数(所有整数均在1∼109范围内),表示整个数列。输出格式输出共一行,包含n个整数,表示排......
  • 并查集
    一:并查集的基本操作:1.初始化:  让每个节点的父节点指向本身 voidinit(){ for(inti=0;i<N;i++)fa[i]=i;}2.查询:用递归的方法查询此节点的根节点,一层一层访问父节点,直至根节点(根节点的标志就是父节点是本身)。要判断两个元素是否属于同一个集合,只需要看它们的......
  • 01数据结构和算法绪论
    01数据结构和算法绪论 soooob 关注2017.10.2318:42* 字数625 阅读2评论0喜欢01.什么是数据结构?数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及他们之间的关系和操作等相关问题的学科。通俗来说数据结构是:程序设计=数据结构+算法再简单的......
  • 数据结构口胡记录
    数据结构口胡记录114514天没写博客了(悲)BearandBadPowersof42\(tag\):线段树,势能分析原问题不好直接做,考虑转化维护信息首先可以发现42的幂次并不多,所以每次操作3到停止的次数并不多,因此可以用线段树多次打区间加标记。问题转化为看一个区间内是否存在42的倍数,因为区间......