集合的表示
- 集合运算:交、并、补、差,判定一个元素是否属于某一集合
- 并查集:集合并、查某元素属于什么集合
- 并查集问题中集合存储如何实现?
- 链式存储:树结构表示集合,树的每个结点代表一个集合元素
2.顺序存储:用数组表示
集合运算
- 查找某个元素所在的集合(用根节点表示)
#define MaxSize 100
typedef int ElementType;
typedef struct {
ElementType Data;
int Parent;
} SetType;
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;
for ( ; S[i].Parent >= 0;i = S[i].Parent);
return i;
}
- 集合的并运算
- 分别找到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;
}
可以利用Parent的信息表达集合中元素的个数(负数绝对值)
随着集合的不断并入,可能会使树越来愈高,从而降低Find的效率,因此尽量将较小的集合并入较大的集合
int ImprovedUnion( SetType S[], ElementType X1, ElementType X2 ){
int Root1, Root2;
int max, min;
Root1 = find( S[], X1 );
Root2 = find( S[], X2 );
if( Root1 != Root2 ){
min = Root1 > Root2 ? Root2 : Root1;
mmax = Root1 > Root2 ? Root1 : Root2;
S[min].Parent = max;
}
}
#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] ); /* 路径压缩 */
}