所谓集合的并查主要是指两种运算,并(把两个集合并在一起)和查(查找某个元素是属于什么集合的),这里有一个经典题目
例子:有10台电脑{1,2,3,...,9,10},已知下列电脑之间已经实现了连接:
1和2,2和4,3和5,4和7,5和8,6和9,6和10
问:2和7之间,5和9之间是否是联通的?
解决思路:
(1)将10台电脑看成10个集合{1},{2},{3},...,{9},{10}
(2)已知一种连接“x和y”,就将x和y对应的集合合并;
(3)查询“x和y是否是连通的”就是判别x和y是否属于同一集合
有一种方法是用集合中的某个元素作为树根,其他元素作为叶子结点,例如
并的时候只要把其根挂在另一个根下就好,这种表示方法叫做双亲表示法,也就是孩子指向其父亲
两个函数分别为
//查找某个元素所在的集合(用根节点表示)
int Find(SetType S[],ElementType X){
//在数组S中查找值为X的元素所属的集合
//MaxSize是全局变量,为数组S的最大长度
int i;
for(i=0;i<MaxSize&&S[i].Data!=X;i++);
if(i>=MaxSize) return -1;//未找到X,返回-1
for(;S[i].Parent>=0;i=S[i].Parent);
return i;
};
//集合的并运算
void Union(SetType S[],ElementType X1,ElementType X2){
int Root1,Root2;
Root1=Find(S,X1);
Root2=Find(S,X2);
if(Root1!=Root2)S[Root2].Parent=Root1;
};
下面对如下图所示的举例
首先是查找结点10和3的根,然后将两个集合合并,再次找其根节点
执行结果是
附上完整代码
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100
typedef int ElementType;
typedef struct{
ElementType Data;
int Parent;//父节点的下标
}SetType;
//查找某个元素所在的集合(用根节点表示)
int Find(SetType S[],ElementType X);
//集合的并运算
void Union(SetType S[],ElementType X1,ElementType X2);
int main(){
SetType S[MaxSize];
int a[10]={1,2,3,4,5,6,7,8,9,10};
int b[10]={-1,0,-1,0,2,-1,0,2,5,5};
for(int i=0;i<MaxSize;i++){
S[i].Data=S[i].Parent=-1;
}
for(int i=0;i<10;i++){
S[i].Data=a[i];
S[i].Parent=b[i];
}
printf("10在的根节点为%d\n",Find(S,10)+1);
printf("3在的根节点为%d\n",Find(S,3)+1);
printf("\n");
Union(S,10,3);
printf("10在的根节点为%d\n",Find(S,10)+1);
printf("3在的根节点为%d\n",Find(S,3)+1);
system("pause");
}
//查找某个元素所在的集合(用根节点表示)
int Find(SetType S[],ElementType X){
//在数组S中查找值为X的元素所属的集合
//MaxSize是全局变量,为数组S的最大长度
int i;
for(i=0;i<MaxSize&&S[i].Data!=X;i++);
if(i>=MaxSize) return -1;//未找到X,返回-1
for(;S[i].Parent>=0;i=S[i].Parent);
return i;
};
//集合的并运算
void Union(SetType S[],ElementType X1,ElementType X2){
int Root1,Root2;
Root1=Find(S,X1);
Root2=Find(S,X2);
if(Root1!=Root2)S[Root2].Parent=Root1;
};
这样做就是会出现一个问题,有可能会把高的树挂在低的树下面,树越来越大的同时也会越来越高,所以会导致Find效率会降低,为了改善合并以后的查找性能,可以采用小的集合合并到相对大的集合中,正好可以修改根节点原本的-1的值,表示其下面挂了几个元素,修改后的Union函数为
//集合的并运算
void Union(SetType S[],ElementType X1,ElementType X2){
int Root1,Root2;
Root1=Find(S,X1);
Root2=Find(S,X2);
if(Root1!=Root2){
if(S[Root1].Parent<S[Root2].Parent){S[Root2].Parent=Root1;S[Root1].Parent+=S[Root2].Parent;}
else {S[Root1].Parent=Root2;S[Root2].Parent+=S[Root1].Parent;}
}
};
哈哈哈哈哈,done