数据结构之不相交集

不相交集(并查集)是解决等价问题的一种有效数据结构。这种数据结构实现起来简单,每个例程只需要几行代码,而且可以使用一个简单的数组实现。
 
  等价关系
  若对于每一对元素(a,b),a,b属于集合S,aRb或为true或为false,则称在集合S上定义关系R,如果aRb是true,那么我们说a与b有关系
 
 等价关系是满足下列三个性质的关系R:
     1.(自反性)对于所有a属于S,aRa;
     2.(对称性)aRb当且仅当bRa
     3.(传递性)弱aRb且bRc,则aRc
 

 等价类:一个元素的等价类是S的一个子集,它包含所有与a有关系的元素。

 不相交集就是将集合S的等价类划分开,使得各个等价类不相交即交集为空。
 不相交集主要有两种运算:
 1.find(a)运算,它返回给定元素所在集合的名字
 2.union(a,b)运算,先执行find运算检验a、b是否在同一个等价类,如果不在就将他们合并
 

 a、b在同一个集合当且仅当find(a)=find(b).

 我们用树来分析他们的查找与合并,具体实现上用数组实现。看下图

一.初始状态每个元素单独为一个集合


初始状态对应数组,下标为元素,值为元素所在集合的标识(可以理解为树的根)



二.现在将4、6合并,合并后4、6组合成一颗树

 

对应数组,元素6的值变成了4


编程实现

//合并a、b所在的集合(根据树的大小即节点个数来合并),即节点少的树成为节点多的树的子树
void Union(int S[], int a, int b) {
	int roota = Find(S, a);
	int rootb = Find(S, b);

	//a、b已经在一个集合
	if(roota==rootb){
		return;
	}
	if (S[roota] > S[rootb]) {
		S[rootb] = S[rootb] + S[roota];
		S[roota] = rootb;
	} else {
		S[roota] = S[roota] + S[rootb];
		S[rootb] = roota;
	}
}

//找到a所在的集合,即返回a的根
int Find(int S[], int a) {
	if (S[a] < 0) {
		return a;
	} else {
		return Find(S, S[a]);
	}
}

测试

int main() {
	int S[9];
	//这里用正数表示根,负数的相反数表示树的节点个数。因为初始时每颗树只有一个节点
	//所以初始化为-1
	for (int i = 0; i < 9; i++)
		S[i] = -1;

	Union(S,2,8);
	Union(S,3,6);
	Union(S,1,3);

	for(int i=0;i<9;i++)
		printf("%d ",S[i]);
	return 0;
}

这里介绍一种更好的Find实现,即在查找过程中,将那些节点的父亲节点值都更新为根节点值

/**
 * 在查找过程中压缩路径
 */
int FindAndCompressPath(int S[],int a){
	if(S[a]<0){
		return a;
	}else{
		return S[a] = FindAndCompressPath(S,S[a]);
	}
}



猜你喜欢

转载自blog.csdn.net/jygqm/article/details/80964135