集合的并查

所谓集合的并查主要是指两种运算,并(把两个集合并在一起)和查(查找某个元素是属于什么集合的),这里有一个经典题目

例子:有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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉风小宇

请我喝个咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值