数据结构5-3:集合及运算

集合的表示

我们可以用一个树来表示一个集合,一棵树是一个集合,如果是两个集合就是两棵树,那么树里的每个节点呢是代表集合中的元素,用树根来代表这个集合,所以想查某个元素属于哪个集合,你只要在这个树上面去找它的根结点是谁,如果要把两个集合并在一起,那么就是把两个树并在一起,形成一个更大的树。

在这里面,我们很重要的一个操作是要查某个元素属于哪个集合,是从这个元素的结点开始去找这个树的树根,也就是说去往上找,我们]要把树并在一起呢,只要把一个根连到另外一个根就可以了。所以在这两个并查操作过程当中,我们不存在己知一个结点找它儿子是谁,而我们更重要的是要知道已知1个结点它父亲是谁。因为根据这个我们可以很快找到根了,所以呢这是一种树的一种表示方法。

双亲表示法:每个结点的指针呢都指向它的父亲。
对于这种存储方式,我们可以用链表进行存储,但是更好的一种方式是使用数组进行存储。

Data代表结点的信息
Parent代表下标,指向父亲的位置。
根结点没有父亲结点,所以将根结点的Parent设为-1.

利用这种结构,我们很容易就知道根结点是谁。

集合运算

查找

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; /* 找到X所属集合,返回树根结点在数组S中的下标 */ }

并运算

Root1、Root2分别表示元素x1与元素x2所在的下标。
通过if语句判断Root1与Root2是否是同一个树根,不是同一个树根才做Union合并操作。

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; }

合并操作,将元素3的父亲结点的下标指向元素1,完成合并。

如果不断地做Union的时候呢,这个树会变得越来越大。而且还有另外一种可能,就是这个树呢越来越高,导致我们的查找Find效率会比较差,所以尽量把小的集合并到大的集合中去。我们要知道哪个集合大,所以我们要知道根结点这棵树下一共有多少个元素,所以就将根结点的Parent的下标改为元素的个数,前面再加一个负号即可。

代码

代码:

#define MAXN 1000                  /* 集合最大元素个数 */
typedef int ElementType;           /* 默认元素可以用非负整数表示 */
typedef int SetName;               /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MAXN]; /* 假设集合元素下标从0开始 */

void Union( SetType S, SetName Root1, SetName Root2 )
{ /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 */
        S[Root2] += S[Root1];     /* 集合1并入集合2  */
        S[Root1] = Root2;
    }
    else {                         /* 如果集合1比较大 */
        S[Root1] += S[Root2];     /* 集合2并入集合1  */
        S[Root2] = Root1;
    }
}

SetName Find( SetType S, ElementType X )
{ /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return S[X] = Find( S, S[X] ); /* 路径压缩 */
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱睡觉的小馨

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值