寒假算法学习(并查集)

概念

并查集(Disjoint Set Union,简称并查集)是一种数据结构,用于管理元素的分组和查找问题。它主要支持两个操作:查找(Find)和合并(Union)

  1. 查找(Find)操作: 用于确定某个元素所属的集合,即找到该元素所在的根节点,从而确定其所属的组。这个操作可以帮助我们确定两个元素是否属于同一个集合。

  2. 合并(Union)操作: 用于合并两个集合,将它们合并成一个更大的集合。通常,我们选择两个集合的根节点,将其中一个根节点的父节点指向另一个根节点,从而将两个集合合并在一起。

并查集通常使用数组来实现,其中数组的每个元素表示一个节点,其值表示指向的父节点。初始状态下,每个节点都是一个独立的集合,即其父节点指向自己。

应用

并查集主要用于解决以下几类问题(网上查的):

  1. 连通性问题:可以用并查集来判断图中的节点是否属于同一个连通分量,或者求图中连通分量的个数。这在网络连接、社交网络分析等场景中非常有用。
  2. 动态图的连接问题:在动态图中,随着边的加入,可以使用并查集来快速判断两个节点是否连通,从而在算法中优化性能。这在网络通信、关系网络等场景中有广泛应用。
  3. 最小生成树算法(如Kruskal算法):Kruskal算法需要判断加入一条边是否会形成环,而并查集可以高效地判断图中是否存在环,从而在构建最小生成树时起到关键作用。
  4. 集合合并问题:并查集可以用来合并集合,将多个小集合合并成一个大集合。这在集合操作、区域合并等问题中很常见。
  5. 元素分类问题:可以利用并查集来快速对元素进行分类,将相同类别的元素归为一类。这在数据聚类、图像分割等领域有广泛应用。

总的来说,并查集是一种高效解决元素分组集合合并连通性等问题的数据结构,在各种算法和应用中都有着重要的作用

多的不说,直接看题。

例题(模板):

P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

如题,现在有一个并查集,你需要完成合并和查询操作。

输入格式

第一行包含两个整数 N,M ,表示共有 N 个元素和 M 个操作。

接下来 M 行,每行包含三个整数 Zi,Xi,Yi 。

当 Zi=1 时,将 Xi 与 Yi 所在的集合合并。

当 Z=2 时,输出 Xi 与 Yi 是否在同一集合内,是的输出 ‘Y’ ;否则输出 ‘N’ 。

输出格式

对于每一个 Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 ‘Y’ 或者 ‘N’ 。

样例输入
4 7
2 1 2
1 1 2
2 1 2
1 3 4
2 1 4
1 2 3
2 1 4

样例输出
N
Y
N
Y

思路:代码注释写的比较全,可以看注释理解思路 

代码: 

#include<stdio.h>
#define M 10000
// 定义一个结构体,表示一个节点,其中data存储整数值,parent用于存储该节点的父节点索引
struct node {
	int data;
	int parent;
} node[M+5];		//用于存储结构体数据
int root[M+5] = {0};		//用于存储每个节点的根索引

int find(int x) {			//用于查找节点x的根节点
	if(node[x].parent != x) {			//如果x的父节点不是其自身,则递归查找其父节点的根节点
		node[x].parent = find(node[x].parent);		// 路径压缩:将x的父节点直接指向其根节点,减少后续查找时间
	}
	return node[x].parent;		// 返回x的根节点
}

void merge(int x, int y) {		//用于合并节点x和y所在的两个集合
	int rootX = find(x);		// 查找节点x的根节点
	int rootY = find(y);		// 查找节点y的根节点
	if(rootX != rootY) {		// 如果这两个根节点不同,则合并两个集合
		node[rootX].parent = rootY;		 // 将一个根节点的父节点设置为另一个根节点,实现合并
	}
}
int main() {
	int n,m;
	scanf("%d%d",&n,&m);
	node[0].data = -1;
	node[0].parent = 0;
	for(int j=1; j<=n; j++) {		//初始化其data为索引值,parent为其自身索引(表示每个节点是其自己的集合)
		node[j].data = j;
		node[j].parent = j;
	}
	while(m--) {
		int z,x,y;
		scanf("%d%d%d",&z,&x,&y);
		if(z == 1) {		// 如果操作类型为1(合并操作),则调用merge函数合并x和y所在的集合
			merge(x,y);
		} else {			// 如果操作类型为1(合并操作),则调用merge函数合并x和y所在的集合
			if(find(x) == find(y)) {	// 如果x和y在同一个集合中(即它们的根节点相同),则输出"Y"
				printf("Y\n");
			} else {					// 如果x和y不在同一个集合中(即它们的根节点不同),则输出"N"
				printf("N\n");
			}
		}
	}
	return 0;
}

模板大致如此,一些类似题可以直接套用模板,但是面对一些复杂的题需要根据具体的题意和要求来用模板,到时代码可能与模板有很大的出入,所以模板不要死记,要在理解的基础上来摸透他的原理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值