并查集是一种简单而有用的数据结构,一般是用数组来实现,数组下标是元素编号,而数组内容存储的是元素所在集合的根(或者是按树形组织下的该元素前驱,而根这个概念本身也是树这一数据结构的概念)
并查集的操作非常简单包括初始化并查集,查询元素所属的集合(用根元素来标识一个集合),合并两个不相交的集合
-
初始化并查集:在以数组下标为元素编号的情况下,初始化需要将每个元素作为一个集合,所以可以将数组元素全部置为-1这一非法下标值,事实上数组下标未根元素的值的绝对值就是集合所含元素个数
-
查询元素所属的集合:数组存储的是下标元素对应的前驱,所以一直往前找到根元素即可
-
合并两个不相交的集合:只需将一个集合的根元素前驱设置为另一集合的根即可
针对并查集有两种优化,都是为了让查询路劲尽可能短,包括路径压缩以及按秩合并
-
路径压缩:在查询某元素的根时,可以将这个元素以及查询路径上的元素的前驱置为根
-
按秩合并:合并时将元素数量小的集合合并到大集合中
所以有如下实现
void init_disjointSet(int S[], int size)
{
for (int i = 0; i < size; ++i)
S[i] = -1;
}
int root_disjointSet(int S[], int size, int x) //这个函数名一般为find
{
if (x < 0 || x >= size)
return -1;
/* 被注释掉的是未优化实现
while(S[x] >= 0)
x = S[x];
*/
int root_x = x;
while (S[root_x] >= 0) // 查询所在集合
root_x = S[root_x];
int current;
while (S[x] >= 0) // 执行路径压缩
{
current = x;
x = S[x]; // x指向其父节点
S[current] = root_x;
}
return root_x;
}
int union_disjointSet(int S[], int size, int x, int y)
{
if (x < 0 || x >= size || y < 0 || y >= size)
return -1;
int root_x = root_disjointSet(S, size, x), root_y = root_disjointSet(S, size, y);
if (root_x == root_y) // 是两个不同集合才能合并
return -1;
/* 被注释掉的是未优化实现
S[root_x] += S[root_y];
S[root_y] = root_x;
*/
if (S[root_x] < S[root_y])
{ // x根下节点多
S[root_x] += S[root_y];
S[root_y] = root_x; // 将小树y加入大树x
}
else
{
S[root_y] += S[root_x];
S[root_x] = root_y;
}
return 1;
}