在一些有N个元素构成的一些单元素的集合,需要反复查找一个元素在哪一个集合中,判断两个数是否属于同一个集合,以及合并两个集合。并查集是一种树型的数据结构。用于处理不相交的集合( disjoint sets)的合并以及查询问题。在使用时用森林表示。
如集合A={0,1,3,6} , B={ 2,4 7} , C={5,8,9}其A,B,C分别构成三棵树,A,B,C共同构成一个森林,并查集一般用数组存储,如数组father。father[i]表示i的父节点,当father[x]==x时,表示x是根结点。在查找元素属于哪一个集合时,采用路径压缩的办法,可以避免树退化为线性,以保证查找的效率。它使用的O(n)的空间,单次查找时间为O(a(n)),一般可认为是常数:
以下是A,B,C集合在father数组的存储:
0 | 0 |
1 | 0 |
2 | 2 |
3 | 0 |
4 | 2 |
5 | 5 |
6 | 0 |
7 | 2 |
8 | 5 |
9 | 5 |
#include<iostream>
using namespace std;
#define n 11
int father[n] = {0,0,2,0,2,5,0,2,5,5,9};
int getRoot(int x);
int Union(int x, int y);
int main(){
/*测试*/
int i,j;
cin >> i;
i = getRoot(i);
cout << i << endl;
Union(1, 10);
cin >> i;
i = getRoot(i);
cout << i << endl;
return 0;
}
int getRoot(int x){
int j = x,i;
while (father[x] != x) /*向上寻找根结点*/
x=father[x];
while (father[j] != x){ /*状态压缩*/
i = father[j]; /*记住j的父节点i*/
father[j] = x; /*将j直接连到根结点*/
j = i; /*更新j为j原来的父节点*/
}
return x; // 返回根结点
}
int Union(int x, int y){
x = getRoot(x);
y = getRoot(y);
if (x != y)
father[x] = y;
return y; //返回最后合并的根结点
}
以上查找还可以使用递归的写法,这样getRoot函数如下:
int getRoot(int x){
if (father[x] != x)
father[x] = getRoot(father[x]); //状态压缩,将沿途结点都连接到根结点后
return father[x]; // 返回根结点
}
以下实现集合的查找以及集合的并: