树结构表示集合+并查集

集合运算:交、并、补、差,判定一个元素是否属于某一集合
采用数组方式储存集合;

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

1.数组中每个元素的结构函数

parent储存双亲结点的下标;
若为根节点,则parent为负数(负数数值代表集合中有多少元素);

typedef struct {
 ElementType Data;
 int Parent;
} SetType;

2.查找某个元素所在的集合

先遍历找到data成员为X的对象;
顺着该对象的parent找上去,直到找到parent为负数的,那个元素的值就是集合名称;

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中的下标 */ 
 }

递归方法查找集合

SetName Find( SetType S, ElementType X )
{ /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return S[X] = Find( S, S[X] ); /* 路径压缩 */
}

3.集合的并运算

分别找到X1和X2两个元素所在集合树的根结点

如果它们不同根,则将其中一个根结点的父结点指针设置成
另一个根结点的数组下标

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

为了使集合不会一端一直变长,使用小集合并入大集合的操作,需要集合头结点增加一个计数器;

并集时,计数器加入较少集合的数量,较少元素集合的头指针指向较大集合的root;

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

你可能感兴趣的:(数据结构,复习)