并查集的基本操作和用法

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 1n,m105

样例

输入样例:
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 1n,m105

样例

输入样例:
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;
}

以上是关于并查集的基本操作和用法的主要内容,如果未能解决你的问题,请参考以下文章

关于并查集的一切全在这里了

并查集的基本运用 POJ1611

数据结构--并查集的原理及实现

CCA算法实现和优化

并查集的基本定义

最基本并查集的实现