普通并查集
路径压缩运用了函数递归,如果数据规模过大,担心爆栈,可以用下面的非递归代码
#include<bits/stdc++.h>
using namespace std;
const int num = 110;
int s[num], height[num];
//初始化
int init(){
for(int i=0; i<=num; ++i){
s[i] = i;
height[i] = 0;
}
}
路径压缩
//int find(int x){
// return s[x]==x? x: s[x]=find(s[x]);
//}
//非递归防爆栈
int find(int x){
int r = x;
while(s[r] != r) r = s[r];
while(x != r){
int temp = s[x];
s[x] = r;
x = temp;
}
return r;
}
void s_union(int x, int y){
x = find(x);
y = find(y);
if(height[x] > height[y])
s[y] = x;
else{
s[x] = y;
if(height[x] == height[y])
height[y]++;
}
}
int main(){
}
带权并查集
int find(x) {
if(x == s[x]) return x;
int pre = s[x];
s[x] = find(s[x]);
val[x] += val[pre];
return s[x];
}
int un(int a, int b, int s) {
int sa = s[x], sb = s[b];
if(sa != sb) {
s[sa] = sb;
val[sa] = val[y] - val[x] + s;
}
}
总结
并查集并没有用到什么STL的工具,只是单单一个数组,就能起到一个分类的作用,一下是并查集的几个要点:
(1)数组的意义,这里定义的parent[]的数组,只是作为一个标签的作用,初始化时值随数组的下标定义,简单方便,代表各节点相互独立,没有瓜葛。
(2)函数parent_union(int x, int y),顾名思义,union联合,通过改变数组parent里的值,使两个两个拥有同一个值,代表这两个属于同一类,起到分类作用。在这个模板的这个函数中,便体现了按秩排序的优化,用秩rank来表示每个节点的高度,在联合时,先判断两个节点的的高度大小,把高度小的依附于高度大的节点,起到尽可能缩小这棵树的高度。
(3)函数find(int x,int y),用于判断两个节点是否相关,是否因为前面的union联合以后归属于同一类。在这个函数中,用到了路径压缩的优化,在优化之前,可以说是一个父节点携带一个子节点,一旦节点一多,将会造成树过长的麻烦,在判断两个节点是否相关时,会使遍历树非常慢,浪费时间。路径压缩就是将所有的子节点依附于同一个父节点,这样在判断起来时只有O(1)的时间复杂度,快了很多。代码的重点在于 parent[x]=find(parent[x])。
(4)函数num(int x)用于查出节点x所属类型一共有多少节点。
(5)路径压缩小心爆栈
并查集中解决函数递归可能造成重复而引起不必要的时间复杂度使用的是路径压缩的方法,而在dp中则是用一个数组将值存放起来,方法可以不同,目的都是为了降低时间复杂度。
2020-01-19 12:58 PigySu