1.什么是并查集
并查集是一种简单的集合表示,通常是将几种不同集合表示成一个具体的概念。比如说人群中有喜欢吃青葡萄的、有喜欢吃紫葡萄的,还有喜欢吃橘子的。并且对这集合的操作只有简单的查找和将其中俩个集合并起来的操作,即为并查集。
2.存储结构
因为并查集里有多个无关的集合,而每一个集合又需要一个具体能代表的操作,因此我们可以选择用树形结构的顺序存储来存储。但是树有孩子表示法,双亲表示法和孩子兄弟表示法。根据并查集的并操作可以了解,用树形结构时就只需要判断每个集合树的根节点,并将其中的一个根结点连接到另一个结点的孩子中,即完成并操作,因此我们需要用双亲表示法的顺序存储来表示。
0 | -1 |
1 | 0 |
3.结构定义
#define SIZE 13;
int UFSets[SIZE];//集合元素数组
4.初始化操作
//初始化
void Initial(int S[]){
for(int i=0;i<SIZE;i++)
S[i]=-1;
}
5.find操作(时间复杂度为O(n))
//在并查集中查找并返回包含元素X的树的根
int Find(int S[],int x) {
while(S[x]>0)
x=S[x];
return x;
}
6.并操作(时间复杂度为O(1))
//求两个不相交子集合的并集
void Union(int S[],int Root1,int Root2){
if(Root1==Root2) //需要两个子集属于不同集合
return;
S[Root2]=Root1; //将Root2连接到另一根Root1下
}
7.优化操作
通过上面代码发现,Find操作与树的高度有关。因此首先优化并操作,通过将小树(高度较低的树)并到大树下,则用双亲表示法时,根结点的值为树的结点树的负数
0 | -结点数 |
//小树合并到大树
void Union(int S[],int Root1,int Root2){
if(Root1==Root2) //需要两个子集属于不同集合
return;
if(S[Root1]>S[Roo2]){
S[Root1]+=S[Root2];
S[Root2]=Root1;
}else{
S[Root2]+=S[Root1];
S[Root1]=Roo2;
}
}
目录
我们可以先找到根节点,然后通过查找根节点的路径,把路径上的节点的双亲表示为根节点,这样优化时间为O(a(n)),接近于常量级的时间复杂度
//在并查集中查找并返回包含元素X的树的根
int Find2(int S[],int x) {
int root=x;
while(S[root]>0)
root=S[root]; //找到根节点
while(x!=root){ //压缩路径
int t=S[x]; //t指向x的父节点
S[x]=root; //修改双亲节点为根节点
x=t;
}
return root;
}