题目介绍:洛谷 P3367 并查集模板
两个数组 s[N] ,height[N] :
s[N] 用来存储哪些结点连通 ( 1 -> 2 == s[2] = s[1],先后顺序很重要
*height[N]用来表示树的高度,用来路径压缩
1*比如说 1 和 2 连通,2 和 3 连通,4 和 5连通
就会有:height [ 1 ] = 1 , height [ 2 ] = 0 , height [ 3 ] = 0 ; height [ 4 ] = 1 , height [ 5 ] = 0;
s [ 1 ] = 1 , s [ 2 ] = s [ 1 ] = 1 , s [ 3 ] = s [ 2 ] = 1 ; s [ 4 ] = 4 , s [ 5 ] = 4;
*如果再加一个 1 和 4 连通 (因为 1 和 4 的高度一样,所以把后面的树接到前面的树上
就会有height [ 1 ] = 2 , height [ 2 ] = 0 , height [ 3 ] = 0 ; height [ 4 ] = 1 , height [ 5 ] = 0;
s [ 1 ] = 1 , s [ 2 ] = s [ 1 ] = 1 , s [ 3 ] = s [ 2 ] = 1 ; s [ 4 ] = s [ 1 ] = 1 , s [ 5 ] = 4;
2*比如说 1 和 2 连通,3 和 4 连通,2 和 3 连通,5 和 6 连通
就会有height [ 1 ] = 2 , height [ 2 ] = 0 , height [ 3 ] = 1 , height [ 4 ] = 0 , height [ 5 ] = 1 , height [ 6 ] = 0 ;
s [ 1 ] = 1 , s [ 2 ] = s [ 1 ] = 1 , s [ 4 ] = s [ 3 ] = 3 , s [ 3 ] = s [ 2 ] = 1 , s [ 6 ] = s [ 5 ] = 5 ;
*如果再加一个 1 和 5 连通 (因为 height [ 1 ] > height [ 5 ] , 所以5 和 1 连通是一样的 ,都是矮树接到高树上
就会有height [ 1 ] = 2 , height [ 2 ] = 0 , height [ 3 ] = 1 , height [ 4 ] = 0 , height [ 5 ] = 1 , height [ 6 ] = 0 ;
s [ 1 ] = 1 , s [ 2 ] = s [ 1 ] = 1 , s [ 4 ] = s [ 3 ] = 3 , s [ 3 ] = s [ 2 ] = 1 , s [ 6 ] = s [ 5 ] = 5 , s [ 5 ] = s [ 1 ] = 1 ;
inte_set( )函数 对两个数组初始化
*在最初时,所以的结点都互不相关,所以s[i]都等于本身 ;而height[N]只要都相等就好;
find_set( x )函数 在构成的集合中寻找 x 的根结点
*都找到最前面的那个结点,故如果两个数在一个集合中,那么他们搜索到的根结点是相同的( 所以可以用find_set ( a ) == find_set ( b ) 来判断他们在不在一个集合中
union_set( x , y )函数 将 x , y 合并(就是 s[ x ] = s [ y ] 的操作函数
*首先都寻找到 x , y 的根,判断是否相等,
相等则把 y 树接在 x 上
不相等则把小的树接在大的树上(路径压缩,用树构成集合
能索引到同一个根,则说明他们是在一个集合内的
#include<bits/stdc++.h>
using namespace std;
int s[20000]={0}; //s[3] = 1,s[2] = 1;表示 1 和 2 连通,2 和 3 连通;
int height[20000]={0}; //树的高度
//int vec[1000]={0};
void inte_set(){ //初始化
for(int i = 1;i <= 20000;i ++){ s[i]=i,height[i]=0 ;}
}
int find_set(int n){ //查找 **路径压缩
if(n!=s[n]) s[n]=find_set(s[n]);
//vec[n]+=vec[s[n]]; //权
return s[n];
}
void union_set(int x,int y){ //合并
x=find_set(x);y=find_set(y);
if(height[x]==height[y]){
height[x]++;
s[y]=x;
}
else if(height[x] > height[y]) s[y]=x;
else s[x]=y;
}
int main(void){
inte_set();
int N,M;cin >> N >> M;
for(int i = 0;i < M;i ++){
int z,x,y;cin >> z >> x >> y ;
if(z == 1) union_set(x , y) ;
else { if(find_set(x) == find_set(y)) cout << 'Y' << endl ;else cout << 'N' << endl ;}
}
// cout << s[1] << " " << s[2] << " " << s[3] << " " << s[4] << " " << s[5] << " " << s[6] << endl ;
// cout << height[1] << " " << height[2] << " " << height[3] << " " << height[4] << " " << height[5] << " " << height[6] << endl ;
return 0;
}
计算有多少个集合:
for(int i = 1;i < n;i ++) if(s[i] == i) sum ++;
如果有负数 , 你可以将最小的负数当做0来处理
如果负数太大 , 可以进行离散化
//低配版
int find(int n){
if(n != _vector[n]) _vector[n] = find(_vector[n]);
return _vector[n];
}
signed main(){
int x = find(n1), y = find(n2);
if(x != y) _vector[x] = y;
return 0;
}