并查集
一、并查集的概念
并查集是一种管理元素分组情况的数据结构,主要实现以下两个功能:
- 查询元素 \(a\) 和 \(b\) 是否在同一集合
- 合并元素 \(a\) 和 \(b\) 所在的集合
注:并查集只能进行合并操作,不能进行分割操作。
二、并查集的实现
一般,我们采用数组 par[]
和 height[]
来实现并查集。
par[x]
:\(x\) 的父节点(若 \(par[x] = x\),则 \(x\) 为根节点)height[x]
:\(x\) 所在树的深度(作用于合并树时防止树的退化)
2.1 init()
首先,要对每一个元素初始化。一开始还未对元素进行分类,所以每个元素独自为一棵树,\(par[x] = x\)。此时,每棵树的深度都为0,\(height[x] = 0\)。
2.2 find()
该函数用于查询元素 \(x\) 的根节点,采用了递归的思想。注意,这里采用了路径压缩,每次查询时,把树中的节点都直接连接到根节点上。
2.3 same()
该函数用于判断两个元素是否属于同一集合,只要查询各自的根节点是否相同就行了。
2.4 unite()
该函数用于合并两个元素所在的集合,核心思想是把一棵树的根节点的父节点改为另一棵树的根节点。这里有两点要注意:第一,要把深度小的树合并到深度大的树中,这样可以防止退化;第二,若合并两棵深度一样的树,合并后,树的深度要加1。
三、代码
// 并查集
#define MAX 10
int par[MAX]; // 该节点的父节点
int height[MAX]; // 树的深度
// 初始化
void init(void)
{
for (int i = 0; i < MAX; i++)
{
par[i] = i; // 每个元素独自构成一棵树
height[i] = 0; // 只有根节点,深度为0
}
}
// 查询元素 x 的根节点
int find(int x)
{
if (par[x] == x) // 父节点为自身,表示为根节点
return x;
else
return par[x] = find(par[x]); // 压缩路径
}
// 查询元素 x 和 y 是否在一棵树中
bool same(int x, int y)
{
return find(x) == find(y);
}
// 合并元素 x 和 y 所在的树
void unite(int x, int y)
{
x = find(x), y = find(y);
if (x == y) // 已在同一棵树中
return;
if (height[x] < height[y]) // 深度小的树合并到深度大的树中,防止退化
par[x] = y;
else
{
par[y] = x;
if (height[x] == height[y]) // 若深度相同,则合并后深度加1
height[x]++;
}
}
完