并查集之基本原理

Background:

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

                                                                                                                        ---- 摘自洛谷某dalao

基本问题:

1、将两个集合合并

2、询问两个元素是否在一个集合当中

 

基本原理: 每个集合用一颗数来表示(不一定是二叉树)。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点。

 

问题1:如何判断树根?  A:if(p[x] == x)

问题2:如何求x的集合编号?   while(p[x] != x) x = p[x];

问题3:如何合并两个集合?  p[x]是x的集合编号,p[y]是y的集合编号。p[p[x]] = p[y]; 

(这里也就是说 把其中一颗树的根节点接到另外一棵树的孩子节点上(x的祖宗节点的父亲节点等于y节点的祖宗节点), 这样就合成了一棵树了)

问题4:如何求一个元素它所在的集合的所有元素的个数(即数的总结点数??)

A: 其实,我们可以在一开始的时候 就令s[i] = 1, 因为刚开始,集合里 只有一个元素,那么集合大小必然为1, 后面要合并集合,我们再使 s[find(x)] += s[find(y)] (find(x) 为查找对应元素祖宗节点的函数,下面会介绍), 因为两个数合并,那么根据上面所述, 会有一棵树在另外一棵树的下面,这样的话,那这颗大树的元素个数就是两颗数的元素个数之和啦! 然后 我们只需要输出 s[find(x)] 就好了, x对应的祖宗(根节点)的那颗树的节点个数!

 

最后再讲一讲find函数:

我们对于find函数 使用了压缩路径的方法:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBASmFja19GZWleX14=,size_20,color_FFFFFF,t_70,g_se,x_16

首先:并查集的单次查询理想复杂度应该是O(logn)的,但是如果有一个这样的数据,并查集的复杂度就是O(n),

为了避免这种情况,我们需对路径进行压缩。

即当我们经过找到祖先节点后,回溯的时候顺便将它的子孙节点都直接指向祖先,使以后的查找复杂度变回nlogn 其实大部分是 0(1).......

具体操作:

while(q[x] != x) q[x] = find(q[x]); return q[x];

 

finish~~~~

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值