首页 > 其他分享 >并查集

并查集

时间:2023-01-22 20:57:35浏览次数:48  
标签:return ll 查集 find 编号 集合 节点

简介

并查集,是一种树形数据结构,用于维护不相交的集合。

基本原理

每个集合用一棵树来表示,树根的编号就是整个集合的编号。
每个节点存储了它的父节点。

实现

模板题(AcWing.836

题目描述

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

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

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

样例

in:

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

out:

Yes
No
Yes

并查集实现

思路

维护一个数组 \(p\) 来表示每个节点的父节点。

判断树根

显然,如果一个节点的父节点是它本身,那它就是根节点。

代码:

inline bool is_root(ll x)
{
    if(p[x]==x) return 1;
    else return 0;
}

这个在本题中用不着。

合并集合

可以发现,如果要将 \(a\) 并入 \(b\) (其实等同于将 \(b\) 并入 \(a\) ),仅需在 \(a\) 的根节点和 \(b\) 的根节点间加入一条边。(原创图)

image

Q:那如果题目数据特别毒瘤,不间断地合并很多集合怎么办?那不就被卡成 \(\operatorname{O(n)}\) 了吗?
A:路径压缩的 find() 只会跑一遍 \(\operatorname{O(n)}\) ,就会重新变为 \(\operatorname{O(1)}\) 。

代码:

p[find(a)]=find(b);

求 \(x\) 的集合编号

递归查找。

inline ll find(ll x)
{
    if(p[x]!=x) p[x]=find(p[x]);//不断查找父节点,直到找到根节点
    return p[x];
}

以上代码添加了路径压缩,也就是 p[x]=find(p[x]) 在找到父节点的同时顺带更新了 p[x] 的值。

图示(原创图):

image

相当于你问你爸爸你的祖先是谁,你爸爸也不知道,爸爸就去问爷爷,然后你的爷爷也不知道,爷爷就去问你的太爷爷,你的太爷爷年纪太大了,啥也不记得,就去问你的祖先。

代码

#include <bits/stdc++.h>
#define ll long long
#define rll register ll
#define cll const ll
#define N 100005
using namespace std;
inline ll read()
{
    rll x=0;bool f=1;static char c=getchar();
    while(!isdigit(c)){if(c=='-') f=0;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return (f==1)?x:-x;
}
inline void write(ll x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+48);
}
ll n=read(),m=read(),p[N];
inline ll find(ll x)
{
    if(p[x]!=x) p[x]=find(p[x]);
    return p[x];
}
int main()
{
    for(ll i=1;i<=n;i++) p[i]=i;
    while(m--)
    {
        char op[2];ll a,b;
        scanf("%s%lld%lld",op,&a,&b);//这里用 read() 会死
        if(op[0]=='M') p[find(a)]=find(b);
        else
        {
            if(find(a)==find(b)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

标签:return,ll,查集,find,编号,集合,节点
From: https://www.cnblogs.com/wangxuzhou-blog/p/17064651.html

相关文章

  • 并查集及简单的力扣例题
    优秀讲解视频,五分钟让你理解并查集的核心:youtube.com/watch?v=ayW5B2W9hfo看完感觉并查集其实也很容易,不是特别艰深的概念并查集的构成:group,element,father/representati......
  • 并查集(Disjoint-Set)
    并查集目录并查集定义模板题目原题链接题目描述输入格式输出格式数据范围分析并查集基本原理实现步骤问题1问题2问题3优化代码实现定义并查集(Disjoint-Set)是一种可以......
  • [并查集]People on a Line
    题目描述ThereareNpeoplestandingonthex-axis.LetthecoordinateofPersonibexi.Foreveryi,xiisanintegerbetween0and109 (inclusive).I......
  • C++ 树进阶系列之嘿!别绕了,这个问题可以使用并查集
    1.前言并查集是一种抽象数据结构,可以从名字上解释其功能。集:操作对象是集合群,并查集是与集合操作有关的数据结构。查:查询元素所在的集合。并:合并元素之间关系的集合。......
  • 算法学习笔记(4): 并查集及其优化
    并查集并查集,Disjoint-Set,或者通俗一点,叫做MergeFind-Set,是一种可以动态维护若干个不重叠的集合,并支持集合之间的合并与查询的数据结构。集体来说,并查集支持下列两个操作......
  • 并查集和最小生成树
    并查集初始化\(O(n)\)intfa[N],szp[N],sze[N],loop[N];//fa根节点,szp点的数量,sze边的数量,loop自环的数量intn,m;//n代表点数,m代表边......
  • 带权并查集的实现及其应用
    带权并查集普通的并查集只能维护每个节点所在集合的编号,带权并查集则可以维护集合内任意一点到根的距离。简单来说,带权并查集支持如下两种操作:在点\(u\)和\(v\)之......
  • 以前做的PPT 并查集-例题示范力扣990
    我的视频题解空间......
  • 并查集
    并查集操作:将两个集合合并。询问两个元素是否在一个集合中。基本原理:每个集合用一棵树来表示。树根的编号就是整个集合的编号。每个节点的值储存它的父节点编号,\(p......
  • Parity Game(奇偶游戏)(POJ1733、AcWing 239)(并查集)(扩展域写法+边带权写法)
    题目小A和小B在玩一个游戏。首先,小A写了一个由0和1组成的序列S,长度为N。然后,小B向小A提出了M个问题。在每个问题中,小B指定两个数l和r,小A回答S[l......