并查集的基本操作和用法
Posted JunMain
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集的基本操作和用法相关的知识,希望对你有一定的参考价值。
并查集
并查集的作用:
1.合并区间
2.判断两个数是否再同一个集合之中
3.寻找联通子图的的个数
并查集的关键函数find(x)与p[]数组 寻找x的祖宗节点
/* find函数用于寻找祖宗节点 */ int find(int x) { if (p[x] != x) find(p[x]) return p[x]; } /* p[N] 存储每个节点的祖宗节点 初始化p[N] 一开始每个节点的祖宗是他自己, 一个数作为一个集合 */ for (int i = 1; i <= n; i ++) p[i] = i; /* 合并集合 (a,b)所在的集合 */ if (find(a) != find(b)) //当不是一个集合才进行合并 p[find(a)] = find(b); //让a的祖宗节点 归顺b的祖宗节点 /* cnt[N]数组存储每个集合元素个数 */ for (int i = 1; i <= n; i ++) cnt[i] = 1; //初始化一开始每个集合只有一个元素 if (find(a) != find(b)) //集合元素变化只会再合并的时候变换 { a = find(a), b = find(b); p[a] = b; cnt[b] += cnt[a]; //b集合中的元素个数要加上a中元素个数 }
合并集合
题目描述
一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。
现在要进行 m 个操作,操作共有两种:
M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。
输出格式
对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。
每个结果占一行。
数据范围
1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1≤n,m≤105
样例
输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes
代码
#include <bits/stdc++.h>
using namespace std;
int f[100010];
int find(int x) // 寻找该点的祖宗节点
{
if (f[x] != x) f[x] = find(f[x]);
return f[x];
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) f[i] = i; //初始化
while (m -- )
{
int a, b;
char ch[2];
scanf("%s%d%d", ch, &a, &b);
if (*ch == 'M') f[find(a)] = find(b); //把a的祖宗节点,改成b的祖宗节点,集合合并
else
{
if (find(a) == find(b)) printf("Yes\\n");
else printf("No\\n");
}
}
return 0;
}
连通块中点的数量
题目描述
给定一个包含 n 个点(编号为 1∼n)的无向图,初始时图中没有边。
现在要进行 m 个操作,操作共有三种:
C a b,在点 a 和点 b 之间连一条边,a 和 b 可能相等;
Q1 a b,询问点 a 和点 b 是否在同一个连通块中,a 和 b 可能相等;
Q2 a,询问点 a 所在连通块中点的数量;
输入格式
第一行输入整数 n 和 m。
接下来 m 行,每行包含一个操作指令,指令为 C a b,Q1 a b 或 Q2 a 中的一种。
输出格式
对于每个询问指令 Q1 a b,如果 a 和 b 在同一个连通块中,则输出 Yes,否则输出 No。
对于每个询问指令 Q2 a,输出一个整数表示点 a 所在连通块中点的数量
每个结果占一行。
数据范围
1 ≤ n , m ≤ 1 0 5 1≤n,m≤10^5 1≤n,m≤105
样例
输入样例:
5 5
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
输出样例:
Yes
2
3
题解
多了一个cnt[N]数组记录每个祖宗节点所包含的节点数量(包括自己), 初始化是1
cnt[N]的变化只发生在两个集合合并的时候, 合并后的祖宗节点cnt加上原来集合cnt的个数
并且多了一层判断 a = find(a), b = find(b) 当a != b 的时候才合并, 一为了防止之前已经合并过的集合再次合并
二为了防止一开始 a == b 的情况
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int f[N], cnt[N];
int find(int x)
{
if (f[x] != x) f[x] = find(f[x]);
return f[x];
}
int main()
{
int n, m, a, b;
string op;
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) f[i] = i, cnt[i] = 1;
while (m -- )
{
cin >> op;
if (op == "C")
{
scanf("%d%d", &a, &b);
a = find(a), b = find(b);
if (a != b)
{
f[a] = b;
cnt[b] += cnt[a];
}
}
else if (op == "Q1")
{
scanf("%d%d", &a, &b);
if (find(a) == find(b)) printf("Yes\\n");
else printf("No\\n");
}
else
{
scanf("%d", &a);
a = find(a);
printf("%d\\n", cnt[a]);
}
}
return 0;
}
以上是关于并查集的基本操作和用法的主要内容,如果未能解决你的问题,请参考以下文章