数据结构《14》----并查集 Union-Find

时间:2022-06-06 13:19:50

描述:

并查集是一种描述解决等价关系。能够方便地描述不相交的多个集合。

支持如下操作    1. 建立包含元素 x 的集合  MakeSet(x)

2. 查找给定元素所在的集合 Find(x), 返回所在集合的代表

3. 将两个不相交的集合合并 Union(s1, s2)

本文参考《数据结构与算法分析,C语言描述》这本书,利用数组,给出了并查集的一个容易实现的版本。

采用了按秩合并 和 路径压缩 的方法加速。

数据结构:

用树表示一个集合,树的跟节点就是这个集合的代表元素。

并采用一维数组来非显示地模拟这个森林:

1. 数组长度是总的集合元素个数;

2. s[i] 表示元素 i 的父亲节点;

3. 如果 s[i] 是 负数,表示 i 就是代表元素; 并且负数的绝对值表示所在树的高度。(按高度压缩)

在这样的定义下,Find(x) 非常容易实现,只要从元素 x, 不断往上找,直到找到根节点返回即可。

数据结构《14》----并查集 Union-Find

数据结构《14》----并查集 Union-Find

// return the disjoint set which x belongs to
SetType Find(ElementType x, DisJointSet s)
{
if(s[x] < 0)
return x;
else
return Find(s[x], s);
}

按高度合并:

注意到,Find(x) 的复杂度是树的高度。

按高度合并就是通过将 矮的树 合并到 高的树作为其孩子,从而避免树过高。

//Assume root1 and root2 are roots
void SetUnion(DisJointSet s, SetType root1, SetType root2)
{
if(s[root1] <= s[root2])//root1 is deeper than root2
{
if(s[root1] == s[root2])
s[root1] --;
s[root2] = root1;
}
else
{
s[root1] = root2;
}
}

路径压缩:

这种方法可以与按高度合并兼容,能够得到较高的效率。

它其实是在 Find(x) 这个函数做文章。

在搜索 x 所在树的 root 节点的过程中,将中途的节点都指向 root.

看了下图,大家就能明白了。

数据结构《14》----并查集 Union-Find

代码非常简单:

// return the disjoint set which x belongs to
SetType Find(ElementType x, DisJointSet s)
{
if(s[x] < 0)
return x;
else
return s[x] = Find(s[x], s);
}

完整的代码如下:

/* dis-joint-set implemented by L.J. in Nov.11, 2013 */
/* union by height */
#include "disjointset.h"
#include <iostream>
using namespace std; const int NumSets = 8;
typedef int DisJointSet[ NumSets + 1];
typedef int SetType;
typedef int ElementType; void Initialize(DisJointSet s)
{
for(int i=NumSets; i>0; --i)
s[i] = 0;
} //Assume root1 and root2 are roots
void SetUnion(DisJointSet s, SetType root1, SetType root2)
{
if(s[root1] <= s[root2])//roo1 is deeper than root2
{
if(s[root1] == s[root2])
s[root1] --;
s[root2] = root1;
}
else
{
s[root1] = root2;
}
} // return the disjoint set which x belongs to
SetType Find(ElementType x, DisJointSet s)
{
if(s[x] < 0)
return x;
else
return Find(s[x], s);
}