C/C++百题打卡[7/100]——【模板】并查集[洛谷]

本文详细介绍了C/C++编程中的并查集数据结构及其应用,通过实例解析了如何使用并查集解决集合合并与查询的问题,同时提供了完整的代码实现。并查集作为一种树形结构,常用于处理不相交集合的合并与查询,文章通过一个生动的故事解释了其工作原理,并讨论了路径压缩优化技巧。
摘要由CSDN通过智能技术生成



Sets ☁️

上一题链接: C/C++百题打卡[6/100]——表格涂色「MCOI-06」Gerrymandering.
百题打卡总目录: 🚧 🚧 …


一、题目总述

在这里插入图片描述

在这里插入图片描述

输入样例

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


二、思考空白区






题目难度:⭐️⭐️⭐️ 普及-








三、题目解析

● 题目的意思也比较明显,关键是我们要用什么样的 数据结构 来构建题目中所说的 “集合”。这里,我们要来学习一下闻名遐迩的 “并查集”。

并查集 [摘自百度百科]:并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。常常在使用中以森林来表示。

一个涵盖了“并查集”的故事:【注意 公司名 的变化】

第一天,市场上有 5 个人(分别是 a、b、c、d、e),他们一开始都是自己的老板,即分别有 5 个单独的集合 “公司1_集合1{a}”、“公司2_集合{b}”、“公司3_集合{c}”、“公司4_集合{d}”、“公司5_集合{e}”,因为他们都在为自己打工赚钱。


第二天,小a 拉 小b 合伙一起干,但 “一山不容二虎”,不能有两个老板,小a 就说:“我做老板吧”。于是 小a 和 小b 组成了一个 “公司1_集合{a, b}”,这个集合中 小b 指向了 小a,因为 小a 做了 “最大的老板”。


第三天,小b 拉 小c 入伙,于是 小c 就不是自己的老板了,他的老板变成了 小b。 随后 “公司1_集合{a, b}” 更新为了 “公司1_集合{a, b, c}


第四天,小d 拉 小e 入伙,小d 做了 “最大的老板”。故有了 “公司2_集合{d, e}”。


第五天,小c 对 小a 说:“大老板,我想喊 小e 入伙”。小c 就去拉 小e 了,小e 就说:“我去问问我的大老板”。结果他的大老板 小d 也同意了,于是 小d 的老板变成了 小c。故两家公司合并成了 “公司1_集合{a, b, c, d, e}

● 这里提第一个问题,在一个 “集合” 中,怎么去找到 “自己最大的老板”?

这个可以通过 “指针+递归” 来处理。“指针” 体现在 “数组” 里。代码如下

注: Boss[..] 是一个数组, x 的老板就是 Boss[x]

int Find_Big_Boss(int x)
{
	if (Boss[x] == x)	// 自己所属最大的老板是自己
		return x;
	else			// 自己所属最大的老板不是自己
	{
		int Big_Boss = Find_Big_Boss(a[x]);	// 通过递归去找到 自己的老板的 “所属的最大的老板”
		return Big_Boss;			// 找到 “最大的老板” 并返回
	}
}

那么上面那个 故事 就可以演示为

在这里插入图片描述

● 但如果按照上述 Find_Big_Boss() 函数来做的话,这这样可能会形成一条长长的链,随着链越来越长,我们想要从底部找到根节点会花越来越长的时间。怎么解决呢?

我们可以使用 路径压缩 的方法。既然我们只关心一个元素对应的根节点,那我们希望每个元素到根节点的路径尽可能短。这只要我们在查询的过程中,把 “沿途每个人(包括自己)的老板” 都设为 “最终找到的那个最大的老板” 即可。下一次再查询时,我们就可以省很多事。这用递归的写法很容易实现:

注: Boss[..] 是一个数组, x 的老板就是 Boss[x]

int Find_Big_Boss(int x)
{
	if (Boss[x] == x)	// 自己所属最大的老板是自己
		return x;
	else			// 自己所属最大的老板不是自己
	{
		int Big_Boss = Find_Big_Boss( Boss[x] );	// 通过递归去找到 自己的老板的 “所属的最大的老板”
		---------------->>> 只改了下面这一句 <<<----------------
		return Boss[x] = Big_Boss;	// 把 “沿途每个人(包括自己)的老板” 都设为 “最终找到的那个最大的老板”
	}
}

那么上面那个 故事 就可以演示为

在这里插入图片描述



四、完整代码

#include<stdio.h>
int a[10010];
int x, y, flag, N, M;	
int Find_Big_Boss(int x)
{
	if (a[x] == x)	// 自己所属的老板是自己
		return x;
	else			// 自己所属的老板不是自己
	{
		int Big_Boss = Find_Big_Boss(a[x]);	// 通过递归去找到 自己的老板的 “所属的最大的老板”
		return a[x] = Big_Boss;	 // 把 “沿途每个人(包括自己)的老板” 都设为 “最终找到的那个最大的老板”
	}
}

int main()
{
	scanf("%d%d", &N, &M);
	for (int i = 1; i <= N; i++) // 初始化
	{
		a[i] = i;	// 自己的老板是自己
	}
	while (M > 0)
	{
		scanf("%d%d%d", &flag, &x, &y);
		int Big_Boss_x = Find_Big_Boss(x);		// x 去找到自己的 “最大的老板”
		int Big_Boss_y = Find_Big_Boss(y);		// y 去找到自己的 “最大的老板”
		if (flag == 1)		// 集合合并操作
		{
			a[Big_Boss_y] = Big_Boss_x;	// 把 y 的 “最大的老板” 的老板 变成 x 的 “最大的老板”
		}
		else if (flag == 2)	// 集合查询操作
		{
			if (Big_Boss_x == Big_Boss_y)	// x 的 “最大的老板” 和 y 的 “最大的老板” 一样
				printf("Y\n");				// 就说明在同一个集合
			else
				printf("N\n");				// 否则不在同一个集合
		}
		M--;
	}
	return 0;
}


五、做题小结与反思

● 学一个东西,学一个算法,有时候一看好像就看懂了,但一上手实践就出 Bug。所以呀,还是要多实践啊。



六、参考附录

[1] 原题地址:https://www.luogu.com.cn/problem/P3367.

[2] 对于“并查集”讲得很不错的一篇知乎:《算法学习笔记(1) : 并查集》.

上一题链接: C/C++百题打卡[6/100]——表格涂色「MCOI-06」Gerrymandering.

百题打卡总目录: 🚧 🚧 …


C/C++百题打卡[7/100]——【模板】并查集 ⭐️ ⭐️ ⭐️
标签:并查集

不定期更新     
   2022/2/21     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一支王同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值