数据结构系列内容的学习目录 → \rightarrow →浙大版数据结构学习系列内容汇总。
10. 集合及运算
10.1 集合的表示
集合运算: 交、并、补、差,判定一个元素是否属于某一集合。
并查集: 集合并、查某元素属于什么集合。并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。
并查集问题中集合存储如何实现?
⋄
\diamond
⋄ 可以用树结构表示集合,树的每个结点代表一个集合元素。
例如,有三个整数集合S1={1,2,4,7}、S2={3,5,8}、S3={6,9,10}。
采用数组存储形式:
负数表示根结点;非负数表示双亲结点的下标。
数组中每个元素的类型描述为:
typedef struct {
ElementType Data;
int Parent;
} SetType;
10.2 集合运算
(1)查找某个元素所在的集合(用根结点表示)
int Find(SetType S[], ElementType X)
{ //在数组S中查找值为X的元素所属的集合
//Maxsize是全局变量,为数组S的最大长度
int i;
for (i=0 ; i < Maxsize && S[i].Data != X; i++); //当S[i].Data = X时,循环结束,i为X的位置下标
if(i >= Maxsize) return -1; //未找到X,返回-1
for (; S[i].Parent >= 0; i = S[i].Parent); //如果i < Maxsize,寻找父结点,直到S[i].Parent < 0为止(根结点的Parent为-1)
return i; //找到X所属集合,返回树根结点在数组S中的下标
}
(2)集合的并运算
⋄
\diamond
⋄ 分别找到X1和X2两个元素所在集合树的根结点;
⋄
\diamond
⋄ 如果它们不同根,则将其中一个根结点的父结点指针设置成另一个根结点的数组下标。
void Union(SetType S[ ], ElementType X1, ElementType X2)
{
int Root1, Root2;
Root1 = Find(S, X1);
Root2 = ind(S, X2);
if (Root1 != Root2) //当x1和x2不属于同一子集时,才需要合并
S[Root2].Parent = Root1;
}
为了改善合并以后的查找性能,可以采用小的集合合并到相对大的集合中。(修改Union函数)
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;
}
}
10.3 并查集的实现
数组存储形式实现并查集的代码如下所示。
#include<iostream>
using namespace std;
#define MaxSize 1000
typedef int ElementType;
typedef struct {
ElementType Data; // 存值
int Parent; // 指向父结点
}SetType;
// 查找
int Find(SetType S[], ElementType X)
{
int i;
for (i = 0; i < MaxSize && S[i].Data != X; i++); // 找到数组中该值对应的下标
if (MaxSize <= i) // 如果没有找到,返回 -1
return -1;
for (; S[i].Parent >= 0; i = S[i].Parent); // 找到该结点的根结点
return i; // 返回根结点在数组S中的下标
}
// 并
void Union(SetType S[], ElementType X1, ElementType X2)
{
int root1 = Find(S, X1); // 找到 X1 的根结点下标
int root2 = Find(S, X2); // 找到 X2 的根结点下标
// 如果根结点的下标不同,说明不是一个集合
if (root1 != root2)
{
S[root1].Parent = root2; // 把X1挂到X2的集合
}
}
int main()
{
SetType S[MaxSize];
// 初始化数组,父结点全部指向-1
for (int i = 0; i < MaxSize; i++)
{
S[i].Data = i + 1;
S[i].Parent = -1;
}
cout << Find(S, 5) << endl;
Union(S, 3, 5);
cout << Find(S, 4) << endl;
cout << Find(S, 3) << endl;
Union(S, 1, 3);
Union(S, 2, 4);
Union(S, 8, 6);
cout << Find(S, 6) << endl;
cout << Find(S, 8) << endl;
system("pause");
return 0;
}
代码运行结果如下图所示。