写在前面:
这一讲我们来讲一个非常实用的小技巧即并查集,一开始理解起来可能需要一点时间,但是它的实际代码其实非常的短,在后面的内容中我们也会用到。
并查集定义
并查集是一种非常精巧而实用的树形数据结构,他主要是处理一些不相交集合的合并和查询的问题。比如我给定一些数字并给定它们属于哪个集合,并且每次询问当中的两个数字时可以告诉我这两个数字是否处于同一个集合,这就是一个典型的并查集问题。
查找
我们可以利用一个数组来存储每个元素的相邻结点:
每次合并两个结点时就只用将一个结点数组中的值改成另一个结点的数值即可,假如我们想合并(4,3),(3,2),(2,1),在图中显示出来就会得到下面的结果:
由此我们也可以发现这样子做如果遇到最坏情况时,会使查找的时间复杂度非常的大,这时候我们可以通过路径压缩来解决这一问题。
路径压缩就是在每次查找时,令查找路径上的每个节点都直接指向根节点。
全部代码
为了方便大家理解,这里放上一个例题:
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
M a b
,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;Q a b
,询问编号为 a 和 b 的两个数是否在同一个集合中;
#include<bits/stdc++.h>
using namespace std;
int fa[100010]; //存储所有结点的父结点
//查找操作
int find(int x)
{
if (fa[x] != x) fa[x] = find(fa[x]); //查找同时进行路径压缩
return fa[x];
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
fa[i] = i;
char op;
int a, b;
while (m--)
{
scanf("%s%d%d", &op, &a, &b);
if (op == 'M') fa[find(a)] = find(b);
else
{
if (find(a) == find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}
如果大家有什么问题的话,欢迎在下方评论区进行讨论哦~