并查集:Union-Find(1)

Disjoint Sets:

  我们都知道Sets(集合)是什么,就是一组非重复元素组成的结构。

  先让我们来看一下Disjoint Sets(非相交集合) :

  Disjoint Sets的意思是一堆集合们,它们相互之间都没有交集。没有交集是指:各个集合之间没有拥有共同、相同的元素。中文称作「分离集」。

  Disjoint Sets 的性质相当特殊。信息学家仔细观察其特性后,精心设计出一套优雅美观的资料结构,可以快速的做集合运算。

  由于每个 Disjoint Sets 指的就是集合们都没有交集,我们就不用考虑交集、差集等等的运算,因为结果很明显。所以只需要考虑 union 、 find 、 split 这三个集合运算:

  union 就是将两个集合做联集,合并成一个集合。 find 就是找找看一个元素是在哪个集合里面。 split 就是把一个元素从一个集合中分离出来。

集合的拆分:

  将一个集合拆分成满足以下条件的集合

  1)S1 ∪ S2 ∪ … ∪ Sk = S

  2)Si ∩ Sj = ?(i ≠ j)

  例:S = {1,2,3,4,5,6};

  拆分1:{1,2},{3,4},{5,6};

  拆分2:{1,2,3,4,5},{6};

  ……

Binary relations :

  S x S is the set of all pairs of elements of S (Cartesian product);

  Example: 若S = {a,b,c},则 S x S = {(a,a),(a,b),(a,c),(b,a),(b,b),(b,c), (c,a),(c,b),(c,c)};

  A binary relation R on a set S is any subset of S x S,R(x,y) means (x,y) is “in the relation” .

Three Properties:

  1) A relation R over set S is reflexive means R(a,a) for all a in S;

  比如:S = {1, 2, 3};

  对于{<1, 1>, <1, 2>, <1, 3>, <2, 2>, <2, 3>, <3, 3>}

  It is reflexive because <1, 1>, <2, 2>, <3, 3> are in this relation.

  2) A relation R on a set S is symmetric if and only if for any a and b in S, whenever if R(a, b) , then R(b,a).

  比如:S = {1, 2, 3};

  对于{<1, 1> , <2, 2> <3, 3> } , it is symmetric.

  3) A binary relation R over set S is transitive means:

  If R(a,b) and R(b,c) then R(a,c) for all a,b,c in S

  比如:S = {1, 2, 3};

  对于{<1, 2> ,<2, 3> , <1, 3>}, It is transitive.

Equivalence relations:

  A binary relation R is an equivalence relation if R is reflexive, symmetric, and transitive.

  Suppose P={S1,S2,…,Sn} is a partition

   – Define R(x,y) to mean x and y are in the same Si

  ? R is an equivalence relation

  Suppose R is an equivalence relation over S

   – Consider a set of sets S1,S2,…,Sn where

   (1) x and y are in the same Si if and only if R(x,y)

   (2) Every x is in some Si

  ? This set of sets is a partition

  若S = {a,b,c,d,e}

  ? One partition: {a,b,c}, {d}, {e}

  ? The corresponding equivalence relation:

   (a,a), (b,b), (c,c), (a,b), (b,a), (a,c), (c,a), (b,c), (c,b), (d,d), (e,e)

并查集(Union-Find:

  并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 进行快速规整。

  在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,适合用并查集来描述。

  并查集在路径、网络、图形连接中得到了广泛应用。

  并查集操作:

  1)创建:创建一个集合的初始拆分,一般仅包含自身,如:

{a}, {b}, {c}, … ,并给每个子集选择一个代表元素。

  2)查找:查找一个元素并返回其所在子集的代表元素

  3)合并:合并两个小子集成一个大子集

  例:

  

  

并查集数据结构:

Up-tree结构:

  从单节点树的森林开始,如下:

  经过合并后:

  up代表一个数组,index代表每一节点,up[index]代表相应的parent,一开始up[index]都初始化为0或-1;合并后其值为对应子集的代表元素。

int find(int x) {
while(up[x] != 0) {
        x = up[x];
    }
    return x;
}
void union(int x, int y){
 up[y] = x;
} 

  显然,查找的最坏情况为O(n),合并的最坏情况为O(1);m次查找和n-1次合并最坏情况为O(m*n),如何优化?

  优化策略:

  1)按大小合并,将小树连接到大树,则查找时间复杂度为O(logn),m次查找和n-1次合并最坏情况为O(m*logn);

  2)路径压缩:在查找时将路径上的节点直接连接到root

按大小合并:

  这里,up[root]不再是0或-1,而是存储该子集大小的相反数,当up[index]为一负值,则其为root。

  查找最坏情况为:O(logn)

  证明:当按照大小合并时,高度为h的up-tree节点至少为2^h,可通过数学归纳法证明。

  此外,还可以按照高度合并。

路径压缩:

  直接来看代码,你就懂了:

int find(i) {
    // find root
    int r = i;
    while(up[r] > 0)
        r = up[r]  

    // compress path
    if (i==r)
    return r;
    int old_parent = up[i];
    while(old_parent != r) {
        up[i] = r;
        i = old_parent;
        old_parent = up[i];
    }
    return r;
}

  单次查找的最坏情况依然为O(logn),但是经过多次查找,路径压缩后,m次查找和n-1次合并最坏情况几乎为O(m+n);

时间: 2024-08-06 03:25:04

并查集:Union-Find(1)的相关文章

POJ 1611 The Suspects 并查集 Union Find

本题也是个标准的并查集题解. 操作完并查集之后,就是要找和0节点在同一个集合的元素有多少. 注意这个操作,须要先找到0的父母节点.然后查找有多少个节点的额父母节点和0的父母节点同样. 这个时候须要对每一个节点使用find parent操作.由于最后状态的时候,节点的parent不一定是本集合的根节点. #include <stdio.h> const int MAX_N = 30001; struct SubSet { int p, rank; }sub[MAX_N]; int N, M; v

并查集(Union Find):实现及其优化(c++)

1.什么是并查集 并查集是用来管理元素分组的数据结构.可以高效进行如下操作: 查询元素a.b十是否在同一组 合并a.b所在的组 并查集可以进行合并操作但不能进行分割操作. 2.并查集的结构 并查集采用多叉树形结构实现,每个元素对应一个结点,每个组对应一棵树.重点关注结整体形成一个树形结构,而不是树的形状等信息. 3.并查集的实现 3.1 初始化 对于并查集,一般采用数组来实现,其中元素为数组的索引,其父辈为数组索引对应内容. 在初始化中,将每个元素父辈设为自己,即自己形成一组,并对用一个rank

【算法学习笔记】41.并查集 SJTU OJ 1283 Mixture

---恢复内容开始--- Description CC非常喜欢化学,并且特别喜欢把一大堆液体倒在一起. 现在CC有n种液体,其中m对会发生反应,现在她想把这n种液体按某种顺序倒入一个容器内,让她获得最刺激的体验,使危险系数尽量大. 我们可以这样计算危险系数,一开始容器内没有任何液体,危险系数为1.每次液体倒入容器时,若容器内已有一种或多种液体会与这种液体发生反应,则危险系数会乘2,否则危险系数不变. 请你求出把这n种液体倒在一起的最大危险系数. Input Format 第一行为两个数n和m.

并查集(union/find)

在计算机科学中,并查集是一种树型的数据结构,其保持着用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.有一个联合-查找算法(union-find algorithm)定义了两个操作用于此数据结构: Find:确定元素属于哪一个子集.它可以被用来确定两个元素是否属于同一子集. Union:将两个子集合并成同一个集合. 因为它支持这两种操作,一个不相交集也常被称为联合-查找数据结构(union-find data structure)或合并-查找集合(merge-find set

HDU 1035 Robot Motion Union Find 并查集题解

本题的类型我一看就想到使用并查集解了,因为要查找是否有环,这是并查集的典型用法. 但是由于本题数据实在是太水了,故此有人使用直接模拟都过了.这让本题降了个档次. 这里使用并查集解.而且可以根据需要简化并查集函数,代码还是很好打的. #include <stdio.h> #include <vector> #include <string.h> #include <algorithm> #include <iostream> #include &l

POJ 2524 Ubiquitous Religions Union Find 并查集

本题是标准的并查集了,最后利用这些集求有多少独立集. 所以这里也写个标准程序过了. 最后查找独立集合: 看有多少个节点的父母节点是自己的,那么就是独立集合了.自己做自己的父母当然最独立的了,没有任何依赖,呵呵. #include <stdio.h> const int MAX_N = 50001; //const int MAX_M = MAX_N/2 * (MAX_N-1) + 1; int N, M; struct SubSet { int p, r; }; SubSet sub[MAX_

并查集(不相交集)的Union操作

在并查集(不相交集)中附加操作\(Deunion\),它实现的功能是取消最后一次\(Union\)的操作. 实现思想 初始化一个空栈,将每一次的\(Union\)操作的两个集合的根和其值\(Push\)入栈:若执行\(Deunion\)操作时,只需要对栈进行\(Pop\)操作即可.在没有路径压缩时,这个策略是有效的:若并查集(不相交集)实现了路径压缩,将使得\(Deunion\)操作很难进行,因为路径压缩有很大的概率将本来属于一个根下的元素连接到另一个根,若此时执行\(Union\)操作时,很难

UVALive 6910 Cutting Tree(并查集应用)

总体来说,这个题给的时间比较长,样例也是比较弱的,别的方法也能做出来. 我第一次使用的是不合并路径的并查集,几乎是一种暴力,花了600多MS,感觉还是不太好的,发现AC的人很多都在300MS之内的过得. 看到他们的做法后,我知道了这个题比较好的做法. 逆向思维的并查集,因为这里只有去边操作,完全可以离线计算,把删边当成加边,然后逆序输出答案即可. 但是,这个却有一个坑,很坑,只有第一次删除的时候,我们才对他进行操作,加边的时候也只能在第一次删除的时候加.当时因为这里,十分困惑-- 这是我无路径压

poj1611 并查集 (路径不压缩)

http://poj.org/problem?id=1611 题目大意: 有一个学校,有N个学生,编号为0-N-1,现在0号学生感染了非典,凡是和0在一个社团的人就会感染,并且这些人如果还参加了别的社团,他所在的社团照样全部感染,求感染的人数. 解题思路: 并查集的变种,实质就是求0所在的强连通图的结点数目. 这道题纠结在数据的输入上,他只是告诉你哪些学生是同一个社团的.这就需要处理一下,我的想法是:如果这个社团有num个孩子,new出一个大小为num的数组,第一个孩子不处理,从第二个孩子起,和

并查集的应用

定义 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示. 应用 若某个朋友圈过于庞大,要判断两个人是否是在一个朋友圈,确实还很不容易,给出某个朋友关系图,求任意给出的两个人是否在一个朋友圈. 规定:x和y是朋友,y和z是朋友,那么x和z在一个朋友圈.如果x,y是朋友,那么x的朋友都与y的在一个朋友圈,y的朋友也都与x在一个朋友圈. 如下图: 代码: //找朋友圈个数 //找父亲节点 int FindRoot(int chi