最近几日理了理学过的很多oi知识。。。发现不知不觉就有很多的知识忘记了。。。

在聊聊并查集的时候顺便当作巩固吧。。。。

什么是并查集呢?

( Union Find Set ) 是一种用于处理分离集合的抽象数据结构类型。

具体一点:

  当我们给出两个元素的一个无序对(a,b)时,需要快速合并a和b所在的集合,这期间需要反复查找出某元素所在的集合,“并”、“查”和“集”三字由此而来。也就是说,并查集的作用是动态地维护和处理集合元素之间的复杂关系。

  在并查集中,n个不同的元素被分为若干组,每组是一个集合,这种集合就叫做“分离集合”。并查集支持查找一个元素所属的集合以及两个元素各自所属集合的合并操作。

例如,我们有这样一个问题:

  一个城镇里居住着n个市民,已知一些人互为朋友,而且朋友的朋友也是朋友,也就是说,如果A和B是朋友,C和B是朋友,则A和C也是朋友,请你根据给出的若干组朋友关系,求出最大的一个朋友圈的人数。

  这就有了并查集的用武之地了,一开始我们把所有人都各自放在一个集合中,然后根据依次给出的朋友关系,查找判断两个人是否属于同一个集合(是否已经是朋友),如果不在同一个集合,则将这两个集合合并成一个集合(行成一个朋友圈),最后看哪个集合的元素最多并输出个数即可。

然而并查集有什么主要操作呢?

  使用并查集首先要记录一组分离的动态集合S = {S1,S2,···,Sn},每个集合还要设置一个代表来识别,代表只是要选择该集合中的某个元素即可,哪一个元素被选作代表是无所谓的,重要的是,如果请求某一动态集合的代表两次,且在两次请求间不修改集合,则两次得到的答案应该是相同的。并查集主要有三种操作:初始化、查找与合并。

  (1)初始化:make-set(x)

  建立一个新的集合,其仅有的成员是x(同时就是代表)。由于各集合是分离的,所以要求x在没有其他集合中出现过。使用并查集前都需要执行一次初始化操作,无论采用何种实现方式,其时间复杂度都是O(n)。

  (2)查找:find-set(x)

  查找一个元素所在的集合,本操作返回一个包含x的集合的代表。查找是并查集的核心操作,也是优化并查集效率的重点。

  (3)合并:merge(x,y)

  将包含x和y的动态集合(假设为Sx和Sy)合并成一个新的集合S,本操作返回集合Sx∪Sy的代表。一般来说,在不同的实现中通常以Sx或者Sy的代表作为新集合的代表。合并之前一般要先判断两个元素是否属于同一集合,这可以通过查找操作来实现。

终于到了并查集的实现了!!

  并查集可以采用数组、链表和树三种数据结构来实现,选择不同的实现方式会给查找操作和合并操作的效率带来很大的差别。

并查集的数组实现:

  实现并查集的最简单的方法就是用数组记录每个元素所属集合的编号,A[i] = j 表示元素i属于第j 类集合,初始化A[i] = i。查找元素所属的集合时,只需读出数组中记录的该元素所属集合的编号A[i],时间复杂度为O(1)。合并两个元素各自所属集合时,需要将数组中属于其中一个集合的元素所对应的数组元素值全部更新为另一个集合的编号值,时间复杂度为O(n)。所以用数组实现并查集是最简单的方法,而且容易理解,实际使用较多。但是,合并操作的代价太高,在最坏的情况下,所有集合合并成一个集合的总代价会达到O(n2)。

并查集的链表实现:

  用链表实现并查集也是一种很常见的手段。每个分离集合对应一个链表,链表有一个表头,每个元素有一个指针指向表头,表明了它所属集合的类别,另设一个指针指向它的下一个元素,同时为了方便实现,再设一个指针last表示链表的表尾。

  因为并查集问题处理的对象往往都是连续的整数,所以一般选择用静态数组来模拟链表,用下标对应集合的元素。具体数据结构体定义如下qwq:

struct node{
int head,next,last;
}S[maxn];

  

  此时,初始化和查找操作的实现就很简单了。

make-set(x){
S[x].head = x;
S[x].next = 0;
}
find-set(x){
return S[x].head;
}

  对于合并操作,我们先假设merge(x,y)的参数是有序的,是把y所属的集合合并到x所在的集合。首先执行查找操作,当出现find-set(x)≠ find-set(y)时,直接将y的表头接到x的表尾,同时将y所在集合的所有元素head值设为find-set(x),x的表尾也设为y的表尾。需要注意的是,last指针只要在表头结点中记录即可,因为每一次查找到find-set(x)都可以得到表头元素,而链表中其他元素记录last值是毫无意义的。

  考虑到输入数据的特殊性,根据以上合并方法,我们总是把y接到x后面,如果y所在的集合非常大,每次复制的代价就会非常高,比如输入数据形如:(2,1),(3,1),(4,1),·····,(n,1),显然y所在的集合就会越来越庞大,此时时间复杂度就会达到O(n2)。不过,我们可以很快滴想到一个优化方法:不妨比较x和y所在集合的大小,把较短的链表接在较长的链表尾部,这样效果是一样的,但时间效率肯定不比原来差。具体实现时可以在node里多设一个number域,用来记录此条链表中成员的个数。显然,number记录在表头元素中即可。将两个链表合并的时候,只需要将链表的number域相加,因此维护起来是非常方便的。这种快速实现的方法称为“加权启发式”合并,这里的权就是指number域。假设有n个元素,则可以证明这种方法合并操作的总次数不超过nlog2n次。

merge(x,y){
x = find-set(x);
y = find-set(y);
if(x.number > y.number)
merge(x,y);
merge(y,x);
}

  

  以上是并查集的两种实现方法qwq。然而最最最重要以及实用的是并查集的树实现。我会在  谈一谈并查集QAQ(下) 中仔细讲解qwq。

  

谈一谈并查集QAQ(上)的更多相关文章

  1. Hdu 2473(并查集删除操作) Junk-Mail Filter

    有木有非常吊 加强 加强版   啊  ,看了都不敢做了   .后来先做了食物链这个我还是看过的.但还是A不掉,没明确神魔意思 .总而言之.大牛的博客是个好东西.我就那么看了一下,还是不懂怎莫办啊,哎, ...

  2. BZOJ4025 二分图 分治 并查集 二分图 带权并查集按秩合并

    原文链接http://www.cnblogs.com/zhouzhendong/p/8683831.html 题目传送门 - BZOJ4025 题意 有$n$个点,有$m$条边.有$T$个时间段.其中 ...

  3. BZOJ 3319: 黑白树 树+并查集+未调完+神题

    Code: #include<bits/stdc++.h> #define maxn 1000003 using namespace std; char *p1,*p2,buf[10000 ...

  4. Gym - 100625G Getting Through 计算几何+并查集

    http://codeforces.com/gym/100625/attachments/download/3213/2013-benelux-algorithm-programming-contes ...

  5. BZOJ 4195: [Noi2015]程序自动分析 [并查集 离散化 | 种类并查集WA]

    题意: 给出若干相等和不等关系,判断是否可行 woc NOI考这么傻逼的题飞快打了一个种类并查集交上了然后爆零... 发现相等和不等看错了异或一下再叫woc90分 然后发现md$a \neq b, a ...

  6. luogu2024 食物链 (并查集)

    把一个点拆成三个,分别对应它的同类.它的猎物和它的天敌,这样的话(以下的相等都是并查集意义上的): 如果令a,b同类,那么a的猎物不能是b的同类,a的天敌不能是b的同类 如果令a吃b,那么a的同类不能 ...

  7. 种类并查集,Poj(1703)

    题目链接:http://poj.org/problem?id=1703 第一次做种类并查集,有的地方还不是很清楚,想了一上午,有点明白了,这里记录一下. 这里我参考的红黑联盟的题解. 关键:种类并查集 ...

  8. poj1703 Find them,Catch them 【并查集】

    做过一些的带权并查集,再来做所谓的"种类并查集",发现好像就顿悟了. 种类并查集与带权并查集实质上的区别并不大. 关键的区别就是种类并查集仅仅是带权并查集再弄个%取余操作而已.然后 ...

  9. HDU3038 How Many Answers Are Wrong —— 带权并查集

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=3038 How Many Answers Are Wrong Time Limit: 200 ...

随机推荐

  1. 在使用VS过程中关于Javascript没有智能提示的解决方法

    问题:编写基本Script代码没有问题,但是在编写DOM代码时候没有智能提示.也就是在编写一般javascript代码时候没有问题,但是要写DOM代码的时候发现没有智能提示,如document等都需要 ...

  2. jenkins+maven配置findbugs+checkstyle+pmd

    一.findbugs+checkstyle+pmd介绍 工具 目的 检查项 FindBugs 检查.class 基于Bug Patterns概念,查找javabytecode(.class文件)中的潜 ...

  3. python-调用自己写的函数

    在同一个目录下的话,直接在代码里添加即可,下面的例子. 同一个目录下有以下: aaa.py ccc.py ddd.py   想在ddd.py里用aaa.py里的函数,就在ddd.py里面开关位置添加 ...

  4. codeforces 1245D(最小生成树)

    题面链接:https://codeforces.com/problemset/problem/1245/D 题意大概是给你一些城市的坐标,可以在城市中建立发电站,也可以让某个城市和已经建好发电站的城市 ...

  5. poj 3281Dining(网络流 拆点)

    题目链接:http://poj.org/problem?id=3281 题目大意:John养了N只奶牛,他为奶牛准备了F个食物和D个饮料,但是每只奶牛只对其中的一些饮料和食物感兴趣,现在请制定一些方案 ...

  6. CDH安装时,无法纳管全部的节点的一个bug

      问题描述: 使用CDH 5.2版本安装时,agent节点有12个.按照安装说明,在各个节点启动cm-agent之后,发现只有6个节点能被纳管.其它的节点总是无法加入纳管中. 在确认防火墙已经关闭后 ...

  7. 无源汇有上下界可行流(ZQU 1590)

    无源汇有上下界可行流(也就是循环流) 模型:一个网络,求出一个流,使得每条边的流量必须>=Li且<=Hi, 每个点必须满足总流入量=总流出量(流量守恒)(这个流的特点是循环往复,无始无终) ...

  8. 微信小程序遮罩层覆盖input失效

    问题:微信小程序中,我们常使用遮罩层,如点击按钮弹出下拉框.弹框等等.若在遮罩层下存在input.textarea.canvas.camera.map.video等标签时,会出现遮罩层覆盖失效的问题. ...

  9. Adobe PS

    1. ctrl + Tab  切换视图窗口 2.shift 拖拽图片,将 2 张图片放在一起 3.切换显示方式 /全屏/带有工具栏 快捷键:F 4. 缩小/放大工具 快捷键: alt + 鼠标滑轮 5 ...

  10. 505,display,float,position之间的关系(有疑问)

    (display属性设置元素如何显示) 如果display取值为none,那么position和float都不起作用,这种情况下元素不产生框 否则,如果position设置框是绝对定位,float的计 ...