附上知识博客:并查集
4步走,初始化,查找,合并,路径压缩
1)初始化
for(int i = 0; i < = N; i++){
father[i] = i; //也可以为-1
}
2)查找
//递推式
int findFather(int x){
while(x != father[x]){
x = father[x];
}
return x;
}
//递归式
int findFather(int x){
if(x == father[x]) return ;
else return findFather(father[x]);
}
3)合并
void Union(int a, int b){
int faA = findFather(a); //找到a的根节点,记为faA
int faB = findFather(b); //找到b的根节点,记为faB
if(faA != faB){ //如果不属于同一个集合
father[faA] = faB; //合并他们
}
}
并查集产生的每一个集合都是一棵树
4)压缩路径
上面的并查集没有忧化。
极端情况下效率很低,现在题目给出的元素数量很多,并且形成了一条链,那么这个查找函数的效率会很低,假如说有10^5个元素,那么我们找第一个元素就需要10^5次查询,而且每次查询都需要查询到最后一个元素。每次都需要10^5,这种计算量太难受了,对了我们可以把当前查询结点的路径上的所有节点的父亲都指向根节点。
(1)按原先的方法获得x的根节点r
(2)重新从x开始走一遍寻找根节点的过程,把路径上的所有结点的父亲全部改为根节点r。
查找时间就会变成O(1)。有人会说,这样改不消耗时间么?,是的消耗,但是一劳永逸啊。会打表么?
//递推式
int findFather(int x){
itn a = x;
while( x != father[x]){
x = father[x];
}
while( a != father[a] ){ //将从a到 father的路径全指向根节点
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
//递归式
int findFather(int v){
if( v == father[v] ) return v;
else{
int F = findFather(father[v]); //递归寻找fagher[v]的根节点
father[v] = F; //将根节点F赋值给father[v]
return F; //返回根节点F
}
}