数据结构中的并查集
文章目录
一、并查集的概念
1.合并、查找、集合
当给出两个元素的一个无序对(a,b)时,需要快速“合并”a和b分别所在的集合,这期间需要反复“查找”某元素所在的集合。
2.概念
在这数据类型中,n个不同元素被分为若干组。每组是一个集合,这种集合叫分离集合,称之为并查集。
二、并查集典型案例
1.问题
A与B是亲戚,B与C是亲戚,问A与C是否为亲戚?
2.问题分析
亲戚关系是一种典型的等价关系。现将构建一个图论模型,每一个人抽象为一个顶点,N个人即N个顶点,输入的亲戚关系数据则抽象为边,M个亲戚关系数据则为N条边,则可抽象为一个(N,M)图模型。最后的问题则为两个顶点是否在同一个连通块中。
3.思路
(1)对N个人都建立一个集合,在图中则画出N个孤立顶点。
(2)每给一个亲戚关系,就将所对应的集合合并,在图中则给关联顶点连边。
(3)最后的提问,则查看两个元素是否在同一个集合中,在图上是否在同一个通路上。
4.特点(从集合论的角度)
(1)操作是在集合的基础上进行的,没必要保存所有的边。
(2)每一步的得到的划分方式是动态的。
三、并查集的运算
编号1~n的n个元素,x是其中一个元素,设并查集为S。
1.初始化并查集
S={S1,S2,···,Sn},每一个动态集合Si(1<=i<=n)仅仅包含一个编号为i的元素,该元素作为集合Si的“代表”。
2.查找x元素所在的集合
返回并查集S中x元素所在集合的代表。
3.合并集合
将S中元素x和y所在的动态集合进行合并成一个新的集合,且合并之前元素x和y所在的动态集合是分离的。
四、算法实现
1.实现方法(树实现)
(1)用有根树表示集合,树中的每一个结点包含集合的一个元素,每一棵树表示一个集合。并查集S为森林,每棵树根作为集合的代表,每棵树均有指向双亲的指针parent,根结点的双亲指针指向自身。
注:树中的指针只是联系集合元素的关系的作用,没有从属关系。
(2)将高度较小的分离集合树作为子树,保证构造的分离集合树高度较低,不会超过log2 n,得到比较平衡的树。
2.实现代码
(1)数根结点结构体
typedef struct node{
int data; //对应编号
int rank; //对应的秩
int parent; //双亲指针
}UFSTree;
其中的秩是近似等于树的高度,用于的树的合并。
(2)并查集初始化
void MAKE_SET(UFSTree t[],int n){
for(int i=0;i<=n;i++){
t[i].data=i;
t[i].rank=0;
t[i].parent=i;
}
}
树的高度(秩)为0,编号为i,双亲指针指向自己。
(3)查找元素x所在的集合
int FIND_SET(UFSTree t[],int x){
if(x!=t[x].parent) return(FIND_SET(t,t[x].parent));
else return(x);
}
找到x所在节点,沿双亲指针找到根结点,双方的节点编号比下方大(秩),除了根结点的双亲指针与编号相等。
(4)合并集合
void UNION(UFSTree t[],int x,int y){
x=Find_SET(t,x);
y=FIND_SET(t,y);
if(t[x].rank>t[y].rank) t[y].parent=x;
else{
t[x].parent=y;
if(t[x].rank==t[y].rank) t[y].rank++;
}
}
秩较小的根双亲指针指向秩较大的根,相等时,只需其中一个指向另一个,合并后的树秩加1.