本次笔记内容:
5.3.1 集合的表示及查找
5.3.2 集合的并运算
集合的表示
- 集合的运算包括:交、并、补、差、判定一个元素是否属于某一集合。
并查集
- 并查集:集合并、查某元素属于什么集合。
- 并查集问题的现实中例子:
有10台电脑{1,2,3,…,9,10},已知下列电脑之间已经实现了连接:
1和2、2和4、3和5、4和7、5和8、6和9、6和10,问:2和7之间,5和9之间是否是连通的?
上例解决思路:
- 将10台电脑看成10个集合{1},{2},{3},…,{9},{10};
- 已知一种连接“x和y”,就将x和y对应的集合合并;
- 查询“x和y是否是连通的”就是判别x和y是否属于同一集合。
并查集问题中的存储实现
- 可以用树结构表示集合,树的每个结点代表一个集合元素。
如上,使用双亲表示法。双亲的下标即代表集合。
如上图,2的父结点为1,1的下标为0,因此Parent为1;对于没有父结点的根结点,其Parent为-1。
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; /* 未找到X,返回-1 */
for (; S[i].Parent >= 0; i = S[i].Parent)
;
return i; /* 找到X所属集合,返回树根结点在数组S中的下标 */
}
集合的并运算
- 分别找到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;
}
Union函数更好的思想
上述实现可能造成树越来越高,降低查找效率。因此,为了改善合并以后的查找性能,可以采用小的集合合并到相对大的集合中的办法。
使用根结点表示集合元素数量即可。