合并集合(并查集应用)

题目传送门:836. 合并集合 

一共有 n 个数,编号是 1∼n1,最开始每个数各自在一个集合中。

现在要进行 m 个操作,操作共有两种:

  1. M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式

第一行输入整数 n 和 m。

接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。

输出格式

对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1≤n,m≤1e5

输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes

试题解析:

题意解析:
  • 合并集合,及查询是否在同一集合。

方法解析:

本题使用并查集的方法,那么什么是并查集呢?

  • 并查集可以进行集合合并的操作(并)
  • 并查集可以查找元素在哪个集合中(查)
  • 并查集维护的是一堆集合(集)

在具体实现中我们该怎么做呢?

  • 首先,我们需要一个p数组来存取集合的代表元素,即祖宗节点,集合中的每个元素的祖宗节点都相同。
  • 在初始化p数组时,每个数字对应的集合都是独立的,我们初始化时是自己集合的父节点为自身,即p[i] = i,i从1开始。
  • 核心处理为find函数,通过此函数可以返回元素的祖宗节点并进行状态压缩(使集合中的每一个新元素对应p的权值都为同一个祖宗节点)。
    int find(int x) { //返回x的祖宗节点+状态压缩
    	if (p[x] != x) p[x] = find(p[x]);
    	return p[x];
    }
  •  当我们进行合并操作时,已知a和b两个元素,我们可以使a元素对应集合的祖宗节点的父节点赋为b元素对应集合的祖宗节点。
    注意:当我们先执行合并操作,改变当前集合祖宗节点后执行查询操作,哪怕没有改变其他元素的p数组权值,我们也可以通过重复查找,直至p[i]=i时,照样会找到集合的祖宗节点。
代码如下:
#include
using namespace std;
constexpr int N = 100010;
int p[N];
int find(int x) { //返回x的祖宗节点+状态压缩
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}
int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = i; i <= n; i++) p[i] = i;
	while (m--) {
		char op[2];
		int a, b;
		scanf("%s%d%d", op, &a, &b);
		if (op[0] == 'M') p[find(a)] = find(b);//a祖宗的爹是b的祖宗
		else {
			if (find(a) == find(b)) puts("Yes");
			else puts("No");
		}
	}
	return 0;
}

 

你可能感兴趣的:(算法)