在一些有 n n n 个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一族的元素所在的集合合并,期间要反复查找一个元素在哪个集合中。此时,要采用一种特殊数据结构——并查集。
-
定义
并查集是一种用于分离集合操作的抽象数据类型。
-
应用
初始时 n n n个元素分属不同的 n n n个集合,通过不断的给出元素间的联系,要求实时的统计元素间的关系(是否存在直接或间接的联系)。元素之间是否有联系,只要判断两个元素是否属于同一个集合;而给出元素间的联系,建立这种联系,则只需合并两个元素各自所属的集合。
-
优化:路径压缩
找完根节点后,在递归回来的时候顺便把路径上元素的父亲指针都指向根节点。
-
操作
-
初始化
for(i=1;i<=n;i++) father[i]=i;
-
寻找根节点编号并压缩路径
递归思想,通过调用
find(father[x])
来寻找集合的根节点,把现在正在访问的这个元素的父亲指针指向上一步返回回来的find(father[x])
,此时的father[x]
是集合中的根节点,再把father[x]
返回给再上面的一层,让其元素的父亲指针也指向father[x]
(即根节点),这样一层接一层,最后这条递归传递路线上所有的元素的父亲指针都会指向那个根节点。首次进行路径压缩时,时间复杂度为 O ( n ) O(n) O(n),但是第二次查询时间复杂度为 O ( 1 ) O(1) O(1),所以综合一下时间复杂度为 O ( log n ) O(\log n) O(logn)。
int find(int x) { if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
-
合并两个集合
void unionn(int x,int y) { x=find(x); y=find(y); father[y]=x; }
-
判断元素是否属于同一集合
bool judge(int x,int y) { x=find(x); y=find(y); if(x==y) return true; else return false; }
-
-
删边
以最少代价删边等价于以最大代价建边。贪心思路排序之后用并查集维护是否应该删边。