并查集实现

Posted by Useless Programmer on April 21, 2018

    并查集的简单实现

章节目录

  1. 简介
  2. 算法描述
  3. 实现

作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢!

简介##

有时候,我们需要将 n 个不同的元素划分成一组不相交的集合. 开始时, 每个元素自成一个单元素集合, 然后按一定的规律将归于同一组元素的集合合并. 在此过程中要反复用到查询某个元素属于哪个集合的运算.适合于描述此类问题的抽象数据类型称为并查集. 并查集也成为 disjoint set.

算法描述##

正如它的名字, 这个数据集合应该支持两种基本操作(并和查操作):

  • Union(Root1, Root2): 把子集合 Root2 并入集合 Root1 中. 其中 Root1 和 Root2 不应该有交集.
  • Find(x): 查找元素 x 所属的集合.
  • 实现并查集的一个典型方法是使用树形结构来表示元素及其所属子集的关系. 其中:

  • 每个集合以一棵树表示;
  • 树中的节点表示属于该集合的一个元素;
  • 所有集合的全集构成一个森林;
  • 我们以一个实例来阐述并查集的工作原理:
    假定我们有一个要划分的集合 S = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 其中 S1 = {0, 1, 2, 3}, S2 = {4, 5, 6}, S3 = {7, 8, 9}. S1, S2, S3 是划分后的集合.

    起初, 我们将集合 S 中每个元素初始化为一棵树, 每棵树种只有一个元素. 为了简单, 我们使用数组保存这些树. 此时, 这些树构成的森林是这样的:

    images

    上图中, 我们用该数组表示一个有三棵树的森林. 我们使用元素在数组中的索引代表该元素, 元素的值(初始化为-1)代表该节点的父节点的索引, -1 代表该节点没有父节点.

    上述集合 S1, S2, S3 所构成的并查集应该如下图所示:

    images
    images

    由上图可知, 0, 1, 2, 3 属于同一元素, 它们的父节点为 0, 数组的第0个元素的值为-4,代表该集合中有四个节点(包含自身), 之所以设为负数是为了表明它没有父节点. 类似的, 可以知道 第二个集合中有三个元素, 元素 5, 6 的父节点为 4. 以此类推.

    有了这样的数据结构之后, 我们看看如何合并两个集合:
    此时我们只需要将其中一个集合的树的根节点置为表示另一个集合的树的根节点的子女即可. 因此 将 S2 集合并入 S2 可表示为:

    images
    images

    对于查找x的操作, 我们只需要直接取数组在x索引处的值, 如果该节点有父节点, 则使用父节点处的值往上查找, 直到找到一处, 其值为负数, 此时此处的索引就是x元素所在集合的树的根节点了.

    算法实现##

    images
    images

    weightedUnionSets 方法的存在是为了避免简单的使用 unionSets 方法合并两个集合可能会导致树的深度过深, 降低查找的效率.

    一个极端的 case 是: 总是将后一个元素作为前一个元素的子节点. 如下图:
    images

    其实这棵树所表示的也是 S1 集合, 但相比较于前面我们给出的树结构来说, 这棵树的查找效率会比较低, 因此我们在 weightedUnionSets 方法中总是将节点较少的树作为节点多的树的子节点, 这样子就可以避免这种极端的情况了.

    END!