本博客的代码的思想和图片参考:好大学慕课浙江大学陈越老师、何钦铭老师的《数据结构》
集合的操作
1 引子
集合运算: 交、并、补、差, 判定一个元素是否属于某一集合
p 并查集:集合并、 查某元素属于什么集合
p 并查集问题中集合存储如何实现?
Ø 可以用树结构表示集合,树的每个结点代表一个集合元素
例如,有三个整数集合
S1={1,2,4,7}
S2={3,5,8}
S3={6,9,10}
如何使用树来表示集合:
我们可以使用双亲表示法,使用孩子指向父亲:
当然我们也可以使用数组来进步表示:
2 集合的运算
2.1 表示集合的数据结构
1 typedef int elementType; 2 3 4 5 typedef struct node{ 6 7 elementType data; 8 9 int parent; 10 11 }coll,*pCollection;
2.2 查找
需求:给定一个元素,给定一个集合数组(多个集合可能在同一个数组中),查找该元素所属于的结合。要求返回集合的根元素的数组下标
int Find( SetType S[ ], ElementType X )
算法思想:
a.首先对数组进行变量,看是否能找元素X,找到,使用i来记录这个元素X的下标
若没有找到,直接返回-1
b.在根据s[i].parent查找i的父节点,在把父节点的下标给i,一直查询到父节点的的parent=-1为止,此时的父节点就是根元素,把集合根节点的数组下标进行返回
代码:
int find(coll s[],elementType element){ int i; for(i=0;i<MAXSIZE&&s[i].data!=element;i++); if(i>=MAXSIZE){/*在数组中没有找到该元素*/ return -1; } for(;s[i].parent>=0;i=s[i].parent); return i; }
2.3 合并集合
需要:给定两个来自不同集合的元素,要求合并这两个集合
返回:返回合并以后的集合的根元素
void Union( SetType S[ ], ElementType X1, ElementType X2 )
算法思想:
a.首先使用find方法找到x1和x2元素的根节点分别为root1和root2
b.让root2节点指向root1节点,即把root2.parent=indexOf(root1)
代码:
1 void unionCollection(coll s[],elementType x1,elementType x2){ 2 3 int root1 = find(s,x1); 4 5 int root2 = find(s,x2); 6 7 if(roo1!=root2){ 8 9 s[root2].parent=root1; 10 11 } 12 13 }
2.4 思考
如果这样一直对集合进行合并,那么树的高度会一直增加,那么不利于树的查找,那么如何减少树的高度呢?
需求:我们让小的集合指向大的集合,那么我们如何判断集合的大小呢?
方法1:如果我们在集合的数据结构中添加一个size的属性,来表示集合的大小。这样是可以解决问题,但是我们发现,其实只有根节点需要size属性,其他元素是不需要size属性的,这样会造成空间的浪费。
方法2:我们使用-1表示集合的根元素,那么我们能否使用使用-x 表示集合的大小,这是一个好的方法。既避免了空间的浪费,也解决了上面的问题。我们只需要在union方法进行修改即可。
上述的代码在:collection.c可见。