前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于一般的并查集求根操作的一组对照研究

关于一般的并查集求根操作的一组对照研究

作者头像
HansBug
发布2018-04-10 17:04:46
6150
发布2018-04-10 17:04:46
举报
文章被收录于专栏:HansBug's LabHansBug's LabHansBug's Lab

说道并查集,大家一定对于以多叉树状结构为基础的并查集并不陌生,最常见的两种写法如下

1 function getfat(x:longint):longint;
2     begin
3         while x<>c[x] do x:=c[x];
4         exit(x);
5     end;
1 function getfat(x:longint):longint;
2     begin
3         if x<>c[x] then exit(getfat(c[x])) else exit(x);
4     end;

其实并查集的核心操作就是一个寻根操作。代码都很短,也很不容易写错,原理也很明显——假如两个点已经到了一个块里的话,那么由于在树结构中,则两者必然有个共同的祖先节点——根节点。。。于是我就这样子学会了并查集

直到某一天在网上看到某神犇在Pascal程序中采用了如下写法——

1 function getfat(x:longint):longint;
2     begin
3         if x<>c[x] then c[x]:=getfat(c[x]);
4         exit(c[x]);
5     end;

这个里面的程序和之前的第一段程序相当类似,但有着很大的区别——这个里面不单单是用递归的方式找到了根节点,同时又变更着各个中间各级父亲节点的父节点——实际上都变成了树的根节点,也就是说每个getfat操作之后,得到的树在此点所涉及的支链上都已经被撸到了根节点上(HansBug:喂喂喂别想歪了),这样子对于一再的重复操作可以起到减少树的高度的神奇作用。

说了这些,于是来一个小小的实验比对下——

准备:一个并查集程序,实现功能——输入N、M、T,表示原来有N堆,M次操作,T(1表示用循环并查集;2表示用简单递归并查集;3表示用最下面的新方法);接下来输入M行,每行"z x y","1 x y"表示将x和y所在块合并,"2 x y"表示查看x和y在不在一起。就是这样一个简单的问题

程序如下:(本程序中均不开inline优化,同时均在我家的烂电脑上进行测试,速度比较逗——这么告诉你们吧,我曾经不止一次提心吊胆地把我在本机上单组数据就运行了快2秒的程序交上BZOJ,结果一瞬间AC,时间是1000ms多一点,吓尿了)

 1 var
 2    i,j,k,l,m,n,t:longint;
 3    c:array[0..1000000] of longint;
 4 function getfat1(x:longint):longint;
 5          begin
 6               while x<>c[x] do x:=c[x];
 7               exit(x);
 8          end;
 9 function getfat2(x:longint):longint;
10          begin
11               if x<>c[x] then exit(getfat2(c[x])) else exit(x);
12          end;
13 function getfat3(x:longint):longint;
14          begin
15               if x<>c[x] then c[x]:=getfat3(c[x]);
16               exit(c[x]);
17          end;
18 function getfat(x,y:longint):longint;
19          begin
20               case y of
21                    1:exit(getfat1(x));
22                    2:exit(getfat2(x));
23                    3:exit(getfat3(x));
24               end;
25          end;
26 begin
27      readln(n,m,t);
28      for i:=1 to n do c[i]:=i;
29      for i:=1 to m do
30          begin
31               readln(j,k,l);
32               case j of
33                    1:c[getfat(k,t)]:=c[getfat(l,t)];
34                    2:writeln(c[getfat(k,t)]=c[getfat(l,t)]);
35               end;
36          end;
37 end.

于是开始很开心的对拍:

1.N=1000 M=1000000 —— 结果发现,循环实现的并查集居然最好,大约1.2s左右,然后两种递归实现的并查集都差不多少,大约都1.3s左右,呵呵呵呵。。。神奇

2.N=100 M=10000000 ——现在的数据一定更容易产生高大的树,一方面合并增多了,另一方面原始的块数少了,也就意味着块会更加集中。。。

于是结果是——最新的方法明显强大,大约8-10秒,而这个时候循环实现的并查集还是比递归的快,大约12s,而递归的就要至少13s啦.。。

其实——在原来的并查集中每次合并操作(c[getfat(x)]:=getfat(y);)都已经将一棵树的根节点直接连接到了另一棵树的根节点上,两棵树实现了简单合并,但是对于两点是否在同一块内的访问操作时,原来的程序没有减少高度,也就是说对于同一个树的状态访问多少次都是一样要跑那么高,这样子完全可以构造出很高大的一棵树(通过节节合并不难构造出),每次都会爬好高的树,当然慢啦。。。而这个将每次的树直接缩节后,那么高的树就只需要跑一次了,以后直接一下子就升到根节点上啦!!!哥就是这么霸气!!!

总结:事实证明了新方法的强大,在关于树的问题上,对于树结构的不断优化也必将不断提高算法的速度——这是关于树的问题亘古不变的一个定则!!!最重要的是有get了一个新技能,可以更开心的刷题啦哈哈哈哈^_^

(附1:随机数生成器)

 1 var
 2         i,j,k,l,m,n:longint;
 3         a:array[0..10000000,1..3] of longint;
 4 begin
 5         readln(n,m);
 6         randomize;
 7         for i:=1 to m do
 8                 begin
 9                         a[i,1]:=random(2)+1;
10                         a[i,2]:=random(n)+1;
11                         a[i,3]:=random(n)+1;
12                 end;
13         assign(output,'bxj1.txt');
14         rewrite(output);
15         writeln(n,' ',m,' ',1);
16         for i:=1 to m do writeln(a[i,1],' ',a[i,2],' ',a[i,3]);
17         close(output);
18         assign(output,'bxj2.txt');
19         rewrite(output);
20         writeln(n,' ',m,' ',2);
21         for i:=1 to m do writeln(a[i,1],' ',a[i,2],' ',a[i,3]);
22         close(output);
23         assign(output,'bxj3.txt');
24         rewrite(output);
25         writeln(n,' ',m,' ',3);
26         for i:=1 to m do writeln(a[i,1],' ',a[i,2],' ',a[i,3]);
27         close(output);
28 end.

(附2:bat小程序,用于对拍,fuckbxj.exe表示随机数生成器)

 1 @echo off
 2 set /a s=0
 3 :1
 4 set /a s=s+1
 5 echo Test %s%
 6 rem 此处两个数分别表示N和M
 7 echo 100 10000000|fuckbxj.exe
 8 echo 循环求并查集
 9 echo.|time
10 type bxj1.txt|bxj.exe >bxj1.out
11 echo.|time
12 echo 递归求并查集
13 echo.|time
14 type bxj2.txt|bxj.exe >bxj2.out
15 echo.|time
16 echo 优化递归并查集
17 echo.|time
18 type bxj3.txt|bxj.exe >bxj3.out
19 echo.|time
20 fc bxj1.out bxj2.out
21 fc bxj1.out bxj3.out
22 pause
23 goto 1
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2015-02-20 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档