MENU

合并集合(并查集)

• June 11, 2021 • Read: 2952 • 算法,学习笔记,算法&题库

一共有 $n$ 个数,编号是 $\rm {1} \sim n$,最开始每个数各自在一个集合中。
现在要进行 $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”。
每个结果占一行。

数据范围展开目录

$\rm{1} \le n,m \le {10^5}$

输入样例展开目录

  • 4 5
  • M 1 2
  • M 3 4
  • Q 1 2
  • Q 1 3
  • Q 3 4

输出样例展开目录

  • Yes
  • No
  • Yes

题解展开目录

(并查集) 数据结构

并查集介绍:

  1. 将两个集合合并
  2. 询问两个元素是否在一个集合当中

基本原理:每个集合用一棵树来表示,树根的编号就是整个集合的编号,每个节点存储它的父节点,p [x] 表示 x 的父节点。

问题 1:如何判断树根:if(p[x] == x)
问题 2:如何求 x 的集合编号:while(p[x] != x) x = p[x];
问题 3:如何合并两个集合:p [x] 是 x 集合编号,p [y] 是 y 的集合编号,p[x] = y;

image.png

图 1 [并查集及其路径压缩优化] 闫学灿

C++ 代码展开目录

  • #include <iostream>
  • using namespace std;
  • const int N = 100010;
  • int n, m;
  • int p[N];
  • int find(int x) //返回x所在集根节点 + 路径压缩优化
  • {
  • if (p[x] != x)
  • p[x] = find(p[x]);
  • return p[x];
  • }
  • int main()
  • {
  • scanf("%d%d", &n, &m);
  • for (int i = 1; i <= n; i++)
  • p[i] = i;//初始化,每个数各自在一个集合
  • while (m--)
  • {
  • char op[2];
  • int a, b;
  • cin >> op >> a >> b;
  • if(op[0] == 'M')
  • p[find(a)] = find(b);
  • else
  • find(a) == find(b) ? puts("Yes") : puts("No");
  • }
  • return 0;
  • }
版权属于:字节星球 / 肥柴之家 (转载请联系作者授权)
原文链接:https://www.bytecho.net/archives/1814.html
本作品采用知识共享署名 - 非商业性使用 - 相同方式共享 4.0 国际许可协议进行许可。

Last Modified: August 29, 2023
Archives QR Code
QR Code for this page
Tipping QR Code
Leave a Comment