首页 > 其他分享 >并查集

并查集

时间:2023-10-07 21:46:57浏览次数:35  
标签:查集 合并 查询 集合 所属 Find

基本的并查集

OI-wiki Link

并查集,一种用于管理元素所属集合的数据结构,形如一片森林,同一棵树内的元素所属同一集合,不同树内的元素不属于同一集合。

将并查集拆分一下。并,合并;查,查询;集,处理的是集合相关内容。

所以并查集一共有两种操作:合并两元素对应集合、查询某元素所属集合(查询它所在树的树根)。


对于每个元素,需要记录它的父亲,用于寻找树根。

如果需要的话,可以记录它的子树大小,一般只有树根需要记录。

初始化

初始时,每个元素都是一棵树,它们自然没有父亲。

如果要记录子树大小的话,则需把它们初始都赋值为 \(1\)。

查询

当我们要查询一个元素 \(x\) 所属集合时,我们需要一路往上跳,直到跳到 \(x\) 所属树的树根,这个元素也就是 \(x\) 所属的集合。

int Find (int x) { // 查询 x 所属集合
  return (fa[x] ? Find(fa[x]) : x); // 如果当前节点还有父亲节点,那么就往上跳,否则返回当前节点
}

合并

假如现在要合并两个元素 \(x,y\) 的所属集合,那么只要先找到两棵树的树根,再任选一个点连向另一点即可。

如果仅仅是告诉你两个元素 \(x,y\) 所属同一集合,那么还需判断 \(x,y\) 是否所属不同集合,否则可能会导致死递归。

void merge (int x, int y) { // 合并 x, y 所属集合
  x = Find(x), y = Find(y); // 找到树根
  if (x != y) { // 只有 x, y 所属不同集合时才能合并
    fa[x] = y; // 连接
  }
}

并查集时间复杂度优化

合并的复杂度在于查询,除了查询以外只用 \(O(1)\),那么查询的复杂度呢?

很明显,我们可以利用合并构造出一种形如链状的并查集,那么在查询时复杂度仍然可以达到 \(O(n)\),并没有达到优化的效果。

那么就要提到合并和查询的优化了。

路径压缩

路径压缩,顾名思义就是把一长条路径给压缩,从而降低时间。

int Find (int x) { // 查询 x 所属集合
  return (fa[x] ? fa[x] = Find(fa[x]) : x); // 如果当前节点还有父亲节点,那么就往上跳,否则返回当前节点
  // 路径压缩,每次往上跳时都把 fa[x] 更新为树根。
}

启发式合并

当合并两个集合时,集合的大小会影响到后续操作,为了在一定程度上优化时间复杂度,可以选择把节点数少的集合连向节点数多的集合,也可以把深度较小的集合连向深度较大的集合。

// 按节点数启发式合并
void merge (int x, int y) { // 合并 x, y 所属集合
  x = Find(x), y = Find(y); // 找到树根
  if (x != y) { // 只有 x, y 所属不同集合时才能合并
    if (sz[x] > sz[y]) { // 启发式合并
      swap(x, y);
    }
    fa[x] = y, sz[y] += sz[x]; // 连接
  }
}

时间复杂度比较玄学,可以自行 bdfs 或者参考 OI-wiki

一般来说路径压缩后的并查集已经不怕 T,但是启发式合并 + 路径压缩后是 \(O(\alpha(n)\times n)\),其中 \(\alpha(n)\) 为反阿克曼函数,是一个增长十分缓慢的函数,一般题目里可以将 \(\alpha(n)\) 视为 \(5\) 左右的一个常数,是完全不可能 T 的。

带权并查集

OI-wiki Link

就是在维护普通并查集的同时维护每个节点连接向它的父亲的边权,路径压缩时记得要更新为一段路径的边权。

例题:P1196 [NOI2002] 银河英雄传说

种类并查集

不要问我为啥没有 OI-wiki Link,因为 OI-wiki 里甚至没有这玩意。

与普通并查集十分相像,主要就是把一个元素分为几个类,可以直接开多个并查集维护。

例题:P2024 [NOI2001] 食物链

在这道题里,由于不确定每个动物的种类,则将其分类讨论,分为三类,处理时多一点细节。

标签:查集,合并,查询,集合,所属,Find
From: https://www.cnblogs.com/wnsyou-blog/p/17746357.html

相关文章

  • 数据结构-并查集
    并查集的使用范围:1.合并集合2.查询两元素是否属于同一集合   高级用法:  3.进行集合划分<带权并查集>  4.连通块块数查询&块内元素个数统计<连通图>  5.撤销合并<可持久化并查集>//本文暂不涉及,我还不会并查集基本操作:#definerep(i,n)for(int......
  • 线段树分治&可撤销并查集
    可撤销并查集按时间顺序用一个栈维护合并信息,撤销时从栈顶弹出合并信息,恢复原状态。并查集查找祖先时不能路径压缩,只能按秩合并。例题:[ABC302Ex]BallCollector容易想到将\(A_i\)和\(B_i\)之间连边。遍历整棵树,用可撤销并查集维护图。为了进一步求得答案,需要注意到该......
  • 可持久化非确定状态AC自动分块维护线段平衡仙人掌优化最小费用最大流预处理混合图上莫
    P8946TheLostSymbol这种类型的dp的特点就是大部分转移形如\(f(i,j)\rightarrowf(i+1,j+1)\)之类的,并且当以上转移出现时原数组被清空,这就可以用一个deque来维护,然后对于全局赋值/全局加,需要对每个位置维护一个时间戳,并记录上一次赋值/加是什么时候,以便标记下传。(貌似......
  • 倍增并查集
    假如说我们有\(n\)个元素,\(m\)次操作。每次操作给定\(x,y,z\),要求对于任意\(0\lei\lez\),将\(x+i\)和\(y+i\)合并,求最后的并查集形态。数据范围是\(10^5\)级别的。我们考虑将\(z\)二进制拆分,那么可以将\(z\)分解为\(O(\logn)\)个\(2\)的整数次幂之和,也就可......
  • poj 1182 食物链---带权值的并查集
    这题就一组数据,用while(scnaf(“%d%d”,&n,&m)!=EOF)..就wa了,我wa了数次,无语了。。带权值的并查集,d[]数组存的是每个点和根节点的关系,同类为d[i]=0; 根节点吃i点为d[i]=1; i点吃根节点为d[i]=2;自己画图感受一下吧!!#include<stdio.h>#include<string.h>#include<stdlib.h>in......
  • hdu-3926-Hand in Hand-并查集
    判断两个图是否同构。。用了并查集。。#include<stdio.h>#include<string.h>#include<stdlib.h>#include<algorithm>#include<queue>#include<stack>usingnamespacestd;#definelllonglongintn,m;structnode{ intfu; intsum; intf......
  • 并查集 堆排序 (9/10)
    并查集模板 注意查找到后查找的节点直接连接根节点#include<iostream>usingnamespacestd;constintN=100010;intp[N];//关键记住find函数intfind(inta){if(p[a]!=a)p[a]=find(p[a]);//不等于根节点就往头结点走,并且等于returnp[a];}intma......
  • (测试)带权并查集
    带权并查集普通的并查集只能维护每个节点所在集合的编号,带权并查集则可以维护集合内任意一点到所在集合根的距离。带权并查集是结点存有权值信息的并查集。相比于一般的并查集,带权并查集需要开辟两个数组:一个是dsu[N],用来判断集合关系;一个是dis[N],用来描述其与根节点的关系。当......
  • [kuangbin带你飞]专题五 并查集
    WirelessNetwork POJ-2236 题意:n台电脑,坐标(x,y),电脑通讯范围为d;一开始,给出所有电脑坐标,然后所有电脑初始状态都是坏的,题目输入两个操作,第一修电脑且这台电脑可对d范围内正常电脑进行通讯了;第二就是查询,两台电脑是否可以通讯?算法:并查集思路:第一次,我直接通过坐标判断,那些电......
  • Cross Swapping CFE (并查集正负集合)
     思路:把每个草做抽象为点, 观察性质:图中对称的2个点,要交换,可以通过2种的操作方式得到, 2个操作异号, 反之2个操作同号通过+-表示和祖父是什么关系,通过并查集来看看当前有没有在同一个集合里面. ......