数据结构——图解并查集(合并树)

什么是并查集

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

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

 实现过程

1.初始化

初始化时,每一个森林只有一个元素,他们之间是相互独立的,其值为-1。

若一个节点的值为负数,则说明它是根节点,那么他的子节点数量就为|n+1|个,如:值为-1的根有|-1+1|=0个子节点,值为-6的根有|-6 +1| = 5个节点。

所有的森林一般使用一个数组来存储,如下图所示:

数据结构——图解并查集(合并树)_第1张图片

2.合并树

在上图中可以看到初始化时每个节点都有一个编号,从0开始,这个编号是隐藏的,其作用是为了可以方便的在二维数组中查找到指定节点,其值为a[ parseInt(节点编号/数组的长度) ][节点编号 % 二级数组的长度]

如7号节点的值为

a[parseInt(7/3)][7%3] = a[2][1]  = -1

若一个节点的值为正数,则该值表示它的上级节点的编号。现在我们来合并树,把3号与6合并,4号与5号合并。

数据结构——图解并查集(合并树)_第2张图片

 可以看到6号节点的值为3,即6号节点指向了3号节点。

相应的3号节点的值也要变化,因为有其他树合并到了该树上,那么3号节点的值就为本来的值加上所合并上来的树的根节点的值,即为-1 + (-1)= -2,表示有|-2+1| = 1个子节点。

到上一步位置,现在的森林从原来的9个树变成了7个树。

那现在我们要合并5号节点和6号节点怎么办,这里要注意一点,如果节点已经指向了其他的节点,那么是不可以使用该节点直接进行节点,我们必须要找到该节点的根节点后,然后再对根节点进行合并。

6->3,4->5,那么在这里我们就是要对树3和树5进行合并。

a[1, 2] = a[1, 2] + a[1, 0]  //-4
a[1, 0] = a[1,2]的编号

数据结构——图解并查集(合并树)_第3张图片

 在合并过程中的问题:

  • 如何判断节点是子节点还是根节点

         只需判断该节点的值得正负情况,若为负数则为根节点,如果整数则为子节点,并且它的上一级节点编号就为该值

  • 如果判断两个节点A与B是否属于同一个树

 要判断是否属于同一个树,那么就要找到该节点的根节点,这里使用递归的思想,每次找到上一级节点,如上级节点不为根节点,则继续向上找,知道找到根节点为止,如果根节点的编号相同则在同一颗树上。

/*
* num: 节点编号
* len: 数组的长度,这里默认该二位数组的属于为一个正方形,如3*3
*/
function search(num){
   if(tree[parseInt(num/len)][num%len]>0)//说明是子节点
       return search(tree[parseInt(num/len)][num%len]);//不能压缩路径路径压缩
   else
       return num;
}
  • 两棵树A与B合并时,是A合并B,还是B合并A

      在合并树之前,我们必须要考虑究竟是谁合并到谁上,为什么要进行这不操作呢,树的深度越深,那么查询的效率就越慢。

当然不只是高度,在数量上也起到很大的影响作用。所以在合并时,通常是将小树合并到大树上。

    通过下面这个例我们就可以直观的看出差距。

数据结构——图解并查集(合并树)_第4张图片

数据结构——图解并查集(合并树)_第5张图片数据结构——图解并查集(合并树)_第6张图片 

通过上图可以明显的看出吧B合并到A上所产生的影响更小。 

结束语

  • 并查集是一种常见的数据结构,应用广泛,如迷宫游戏
  • 欢迎访问本人个人博客:https://www.dzyong.com

 

 

你可能感兴趣的:(数据结构,数据结构,并查集,合并树)