zoukankan      html  css  js  c++  java
  • 简单并查集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。。。。

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

  • 相关阅读:
    15. DML, DDL, LOGON 触发器
    5. 跟踪标记 (Trace Flag) 834, 845 对内存页行为的影响
    4. 跟踪标记 (Trace Flag) 610 对索引组织表(IOT)最小化日志
    14. 类似正则表达式的字符处理问题
    01. SELECT显示和PRINT打印超长的字符
    3. 跟踪标记 (Trace Flag) 1204, 1222 抓取死锁信息
    2. 跟踪标记 (Trace Flag) 3604, 3605 输出DBCC命令结果
    1. 跟踪标记 (Trace Flag) 1117, 1118 文件增长及空间分配方式
    0. 跟踪标记 (Trace Flag) 简介
    SpringBoot + Redis + Shiro 实现权限管理(转)
  • 原文地址:https://www.cnblogs.com/bobble/p/6379190.html
Copyright © 2011-2022 走看看