zoukankan      html  css  js  c++  java
  • 并查集知识总结

    并查集(union-find sets)

    并查集所基于的数据结构是树形的数据结构,

    整个树被抽象成为集合(Set),

    而 树形结构中,节点被抽象为Elem,

    树的根作为树中节点的祖先,

    在树中,节点除了根节点以外都有父节点。

    但是祖先节点只有一个。

    即在集合Set中所有的元素Elem都有父节点,

    但是它们的祖先节点有且只有一个:可以根据这一条件来判断两个元素

    是否位于同一个集合中,即判断它们的祖先节点是否是相同的即可。

    如果集合中只有一个节点那么,该集合的祖先节点为它本身。

     

    基于该树形数据结构的主要三种原子操作有:

    1.Make_Set(Elem x)

    在刚开始接收元素的时候,每个元素之间都是相互孤立的集合。

    这个操作用于把每一个元素初始化为一个仅包含一个元素的集合。

    在初始化后每一个元素的父节点是它本身。

    每个元素的祖先节点也是它本身。这里需要注意的是,父节点不一定是

    祖先节点的。

    也就是说

    i-(root)>j->k

    k的父节点是j,k的祖先节点是i

    j的父节点是i,j的祖先节点仍然是i

     

    2.Find_Set(Elem x)这个操作时查找一个元素所在的集合。

    这个原子操作的本质思想就是找到元素x的祖先节点,

    祖先节点对于一个集合来所是唯一的,

    就像是对于一个树来所它的根可以

    唯一确定一个树一样。

    所以x元素的祖先节点所确定的集合一定是x所在的集合。

     

    3.Union(Elem x, Elem y) 这个操作使用来合并元素x  和元素 y所在的集合的。

    合并两个集合的思想是,

    各自调用Find_Set(x) 和Find_Set(y)这两个操作,

    找到两个元素所在集合的祖先,

    然后根据树形结构的树的高度作为判断条件来

    使得一个集合的祖先作为另一个集合的祖先。

    这样就实现了合并两个元素所在的集合这一个操作。

     

    即(x所在集合的祖先节点|变为子节点)->(y所在集合的祖先节点|仍旧是父节点&祖先节点)

    或是(y所在集合的祖先节点|变为子节点)->(x所在集合的祖先节点|仍旧是父节点&祖先节点)

     

     

     

     并查集优化操作

    1.递归查找祖先可以使得代码简洁易懂,但是如果集合对应的树形结构比较特殊的时候(例如每层仅有一个节点)

    会消耗很多资源,效率也会下降,会使得最坏情况的时间空间复杂度大大加大。

    所以可以在递归寻找祖先的时候,把最后一个节点的父节点设为父节点的父节点。

    以此类推下去,会将树中所有节点都指向祖先节点,

    这时候的树高为1(不算根节点的情况下):即所有的节点的父节点都是祖先节点了。

     

    这就是所谓的路径压缩方法。

     

    如果说并查集就不得不说并查集中的秩,

    秩往往用来表示子树的深度或者是同一个集合里面的元素的个数。

    2.而在合并集合的时候,通常是将元素少的集合合并到元素多的集合中,

    这样合并后树的高度会相对较小。

     

    下面是对于并查集比较经典的代码实现,

    注释是LZ根据自己的理解增添上去的。

     

     1 int father[MAX];
     2 //这个数组是用来存放某一个元素的父节点元素值的
     3 //即,若设节点i的父节点是节点j的话,则由 father[i]=j;
     4 
     5 int rank[MAX];
     6 
     7 //这个数组是用来记录秩的,在本实现并查集的代码中
     8 //秩是用于记录子集中元素个数的
     9 //即 若元素节点i有k个子节点,那么就有 rank[i] = k;
    10 
    11 
    12 //这个是初始化集合操作,在刚开始录入数据的时候,
    13 //每个元素节点都是一个独立的集合,它们有0个子节点(因为自己无法是自身的子节点,
    14 //但是自己却可以是自己的祖先节点 和 可以是自身的父亲节点)
    15 
    16 //但是具体的实现方法和初始化设定还需要根据具体情况进行推敲
    17 
    18 void Make_Set(int x)
    19 {
    20   
    21   father[x] = x;//将自己设定为自己的父节点(祖先)
    22   rank[x]  = 0; //集合中x的子集个数为0,故将其赋值为0       
    23 }
    24 
    25 
    26 
    27 
    28 //下面的代码在实现递归查找x元素所在集合的同时,
    29 //又对代码进行优化处理,在回溯的时候,又对路径进行了相应的压缩
    30 //其实就是,为了降低树的高度,将集合中的的x节点的父节点设成该集合的祖先节点(树的根)
    31 //不仅仅如此,x父节点的父节点....一直到祖先节点,它们的父节点都是集合的祖先节点
    32 //(其中祖先节点的父节点就是本身,所以  father[祖先]=祖先,循环会因为这个条件而停下来)
    33 
    34 int Find_Set(int x)
    35 {
    36     if(x != father[x])   //如果x的父节点不是它本身的话,x所在的集合中就一定存在其余元素节点
    37     {
    38        father[x]  = Find_Set(father[x]);
    39 //继续寻找x父节点的父节点,一直会找到祖先节点
    40 //一次类推的话,最后从x到x父节点...祖先节点,的父节点都会是祖先节点
    41     }    
    42 //循环会判断到father[x]=x这个地方停止,此时的x就是自身的父节点,即x就是祖先节点
    43 
    44     return father[x];  
    45 //递归到x=father[x]停止,所以由此可知return 的father[x]即是
    46 //在一开始传入操作的形参x所在集合的祖先节点
    47 }
    48 
    49 
    50 
    51 
    52 3.下面的这个操作是要合并传入形参x和y所在的集合
    53 
    54 void Union(int x , int y)
    55 {
    56     x = Find_Set(x);  //首先找到x所在集合的祖先节点
    57     y = Find_Set(y);  //然后找到y所在集合的祖先节点
    58    
    59 
    60 
    61     if(x==y)   return;
    62 //如果x、y值相等的话,说明 一开始传入形参的x y元素拥有同一个祖先,即x,y在相同的集合中
    63   
    64 
    65 
    66 //如果不相同的话,说明x、y在不同的集合中,可以合并
    67 //根据Union的描述,首先比较x和y(ps:此时的x、y分别对应的是传入参数的相应的祖先节点
    68 //所以rank[x]的值是 传入参数1 所在集合的元素总数
    69 // rank[y]的值对应的是 传入参数2 所在集合的元素总数)
    70 //将元素少的集合的祖先节点作为 元素多的集合的祖先节点的 子节点
    71 //即,if对应的是将y所在的集合的祖先节点作为 x集合祖先节点的子节点
    72 
    73     if(rank[x]   >    rank[y])
    74      {
    75         father [y] = x;
    76     }
    77 
    78 
    79 
    80 
    81 //其余的情况,小于或等于都是y所在集合包含x所在的集合
    82 
    83     else
    84     {
    85         if(rank[x] == rank[y])
    86         {
    87               rank[y]++;
    88          }
    89 
    90         father[x]=y;
    91 
    92     }
    93 }

     

     

     

     

     

     

     

     

     

     

    ------------ 能改变现状的,只有战斗的觉悟 ------------
  • 相关阅读:
    冲刺阶段站立会议每日任务3
    冲刺阶段站立会议每日任务2
    典型场景描述
    冲刺阶段站立会议每天任务1
    第七周学习进度
    第六周学习进度
    四则运算网页版
    团队项目简介(1000字)
    返回一个二维整数数组中最大联通子数组的和
    本周学习进度
  • 原文地址:https://www.cnblogs.com/inuyasha1027/p/algorithm_unionFind.html
Copyright © 2011-2022 走看看