• 简单并查集QwQ


    过了好几个月了,把并查集复习一下QwQ

    按子树中的节点数合并的方法 来源于:http://www.cnblogs.com/cyjb/p/UnionFindSets.html   by CYJB  

    模板:

      按节点数合并

     1 procedure init();
     2 begin
     3   for i:= 1 to n do uset[i]:=-1;
     4 end;
     5 
     6 
     7 function find(apple:longint):longint;
     8 begin
     9     if (uset[apple]<0) then exit(apple);
    10 
    11     uset[apple]:=find(uset[apple]);
    12     find:=uset[apple];
    13 end;
    14 {
    15 //非递归版本
    16 function find(apple:longint):longint;
    17 var p,t:longint;   //p=pin, t=temp
    18 begin
    19     p:=apple;
    20     while (uset[p]>=0) do p:=uset[p];//找根节点
    21     while (apple<>p) do
    22     begin
    23         t:=uset[apple];//备份他的爸爸
    24         uset[apple]:=p;//更新当前指向根节点
    25         apple:=t;      //使下一次迭代来更新它爸爸
    26     end;
    27     exit(apple);
    28 end;
    29 }
    30 procedure union(aa,bb:longint);
    31 var
    32    t1,t2:longint;
    33 begin
    34    t1:=find(aa);
    35    t2:=find(bb);
    36    if t1=t2 then exit;
    37    if uset[t1]<uset[t2] then begin
    38                                 inc(uset[t1],uset[t2]); //统计根节点的点的个数
    39                                 uset[t2]:=t1;           //合并树
    40                               end
    41     else begin
    42            inc(uset[t2],uset[t1]); //统计根节点的点的个数
    43            uset[t1]:=t2;           //合并树
    44          end;
    45 end;
    View Code

      路径压缩+启发式合并:

    procedure init();
    begin
      for i:= 1 to n do father[i]:=i;
      for i:= 1 to n do rank[i]:=0;
    end;
    
    function find(apple:longint):longint;
    begin
        if father[apple]<>apple then father[apple]:=find(father[apple]);
        exit(father[apple]);
    end;
    
    procedure union(aa,bb:longint);
    begin
        t1:=find(aa);
        t2:=find(bb);
        if t1=t2 then exit;
        if rank[t1]>rank[t2] then father[t2]:=t1
         else begin
                father[t1]:=t2;
                if rank[t1]=rank[t2] then inc(rank[t2]);
              end;
    end;
    View Code

    主要思想以及注意事项:

    初始化father

    先读入a,b, 然后找两个点的father,    然后合并(b接着a)

    else : 在luogu看到有这么一个挺好的想法,就是不用初始化,在find过程里面如果father[apple]<>0就find(father[apple])否则返回apple

    两个优化:

    1 .路径压缩

    并查集运用树形结构,一个集合便是一棵树,根节点就是father

    方法: 找father的时候沿途可以顺带给该集合内的所有元素记录下找到的father ,不增加时间复杂度,却使得今后find的操作比较快。    //    更新所有子树的father

    注意:在判断时还是要每次都要在递归找一次father再判断//所谓优化只是减少一点检查时递归查找的路径

    假设下面的情况,我们现在如果要给1和2连起来,那么只会更新2的father为1,后面3和4的father还是2, 那优化有什么用呢,假设之前还有4和3的连线,如果按照原始法judge时是4-3-2-1,而优化版的很明显是4-2-1

    2.启发式合并按秩合并

      

       让深度小的数成为深度较大的树的子树——将比较矮的树作为子树,将它的树根指向较高树的树根。

    find+路径压缩:   【事实证明递归查找比非递归查找的版本快,别问我怎么知道。。。】

    function find(apple:longint):longint;
    begin
        if father[apple]<>apple then father[apple]:=find(father[apple]);
        exit(father[apple]);
    end;

    union+启发式合并按秩合并:

    procedure union(aa,bb:longint);
    begin
        t1:=find(aa);
        t2:=find(bb);
        if t1=t2 then exit;
        if rank[t1]>rank[t2] then father[t2]:=t1  //如果有统计子树(集合)节点数记得加上,下面也是
         else begin
                father[t1]:=t2;
                if rank[t1]=rank[t2] then inc(rank[t2]);
              end;
    end;

    judge:

    function judge(x,y:longint):Boolean;
    begin
      if find(x)=find(y) then exit(true)
        else exit(false);
    end;

    其他

    除了按秩合并,并查集还有一种常见的策略,

    就是按集合中包含的元素个数(或者说树中的节点数)合并,

    将包含节点较少的树根,指向包含节点较多的树根。

    这个策略与按秩合并的策略类似,同样可以提升并查集的运行速度,而且省去了额外的 rank 数组。

    这样的并查集具有一个略微不同的定义,

    即若 uset 的值是正数,则表示该元素的父节点(的索引);若是负数,则表示该元素是所在集合的代表(即树根),

    而且值的相反数即为集合中的元素个数。//指的是值为负数的情况

    ——引用自《并查集》by CYJB

    小树指向大树 !!!!!

    procedure init();
    begin
      for i:= 1 to n do uset[i]:=-1;
    end;
    
    
    function find(apple:longint):longint;
    begin
        if (uset[apple]<0) then exit(apple);
    
        uset[apple]:=find(uset[apple]);
        find:=uset[apple];
    end;
    {
    //非递归版本
    function find(apple:longint):longint;
    var p,t:longint;   //p=pin, t=temp
    begin
        p:=apple;
        while (uset[p]>=0) do p:=uset[p];//找根节点
        while (apple<>p) do
        begin
            t:=uset[apple];//备份他的爸爸
            uset[apple]:=p;//更新当前指向根节点
            apple:=t;      //使下一次迭代来更新它爸爸
        end;
        exit(apple);
    end;
    }
    procedure union(aa,bb:longint);
    var
       t1,t2:longint;
    begin
       t1:=find(aa);
       t2:=find(bb);
       if t1=t2 then exit;
       if uset[t1]<uset[t2] then begin
                                    inc(uset[t1],uset[t2]); //统计根节点的点的个数
                                    uset[t2]:=t1;           //合并树
                                  end
        else begin
               inc(uset[t2],uset[t1]); //统计根节点的点的个数
               uset[t1]:=t2;           //合并树
             end;
    end;

    例题:

    简单入门: luogu P1551亲戚   简单并查集裸题

                  luogu P1892 团伙   注意怎样处理敌人的敌人就可以了

            luogu P1455 搭配购买    01背包+并查集

                               格子游戏    理解题意+维度转换

                  luogu P2814 家谱    还没AC。。。。

                               打击犯罪    暴力+并查集+逆向思维

  • 相关阅读:
    Python 爬虫 解决escape问题
    python 爬虫 重复下载 二次请求
    iOS开发-消息通知机制(NSNotification和NSNotificationCenter)
    iOS开发-UITableView自定义Cell
    iOS开发-自定义UIAlterView(iOS 7)
    iOS开发-CocoaPods实战
    iOS开发-UICollectionView实现瀑布流
    iOS开发-UITabBarController详解
    iOS 开发-Certificate、App ID和Provisioning Profile之间的关系
    iOS开发-View中frame和bounds区别
  • 原文地址:https://www.cnblogs.com/bobble/p/6379190.html
Copyright © 2020-2023  润新知