简单并查集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。。。。

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

 

posted @ 2017-02-09 19:56  bobble  阅读(314)  评论(0编辑  收藏  举报