并查集

并查集

img

并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的 合并查询 问题。 它支持两种操作:

  • 查找(Find):确定某个元素处于哪个子集;
  • 合并(Union):将两个子集合并成一个集合。

查找

int fa[MAXN];  // 记录某个人的爸爸是谁,特别规定,祖先的爸爸是他自己
int find(int x) {
  // 寻找x的祖先
  if (fa[x] == x)  // 如果x是祖先则返回
    return x;
  else
    return find(fa[x]);  // 如果不是则x的爸爸问x的爷爷
}

路径压缩

把在路径上的每个节点都直接连接到根上,这就是路径压缩。

img

int find(int x) {
  if (x != fa[x])  // x不是自身的父亲,即x不是该集合的代表
    fa[x] = find(fa[x]);  // 查找x的祖先直到找到代表,于是顺手路径压缩
  return fa[x];
}

合并

把一个祖先合并到别人祖先下,成为儿子。

img

void unionSet(int x, int y) {
  // x 与 y 所在家族合并
  x = find(x);
  y = find(y);
  fa[x] = y;  // 把 x 的祖先变成 y 的祖先的儿子
}
//时间复杂度O(n) ,递归程序
const int N=10010;
int s[N];
void init_set(){//初始化 
    for(int i=1;i<=N;i++) s[i]=i;
}

int find_set(int x){//查找 
    return x=s[x]?x:find_set(s[x]);//递归查找 
}

void union_set(int x,int y){
    x=find_set(s[x]);
    y=find_set(s[y]);
    if(x!=y) s[x]=s[y];
} 

按树的高度合并

int s[N],height[N];
//时间复杂度O(logn) 
void init_set(){//初始化 
    for(int i=1;i<=N;i++) 
    s[i]=i,height[i]=0;
}

int find_set(int x){//查找 
    int r=x;
    while(s[r]!=r) r=s[r];
    int i=x,j;
    while(i!=r){
        j=s[i];
        s[i]=r;
        i=j;
    }
    return r;
}

void union_set(int x,int y){
    x=find_set(s[x]);
    y=find_set(s[y]);
    if(height[x]==height[y]){
        height[x]=height[x]+1;//合并树的高度加一 
        s[y]=x;
    }
    else{
        if(height[x]<height[y]) s[x]=y;
        else s[y]=x;//矮树合并在高树上,高度不变 
    }
} 

题目

洛谷P3367

考察并查集的两个基本操作。这题要关流。

#include<bits/stdc++.h>
using namespace std;
#define ios ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
#define sc(n) scanf("%lld",&n)
#define SC(n,m) scanf("%lld%lld",&n,&m)
#define pr(n) printf("%lld\n",n);
int f[100010];

int find(int x){
	if(f[x]==x) return x;
	return find(f[x]);
}

int main(){
	ios
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	while(m--){
		int op,x,y;
		cin>>op>>x>>y;
		if(op==1){
			f[find(x)]=find(y);
		}
		else{
			puts(find(x)==find(y)? "Y" : "N");
		}
	}
	return 0;
}

HDU1213

#include<bits/stdc++.h>
using namespace std;

const int N=100010;

int s[N],height[N];
//时间复杂度O(logn) 
void init_set(){//初始化 
    for(int i=1;i<=N;i++) s[i]=i;
}

int find_set(int x){//查找 
    return x==s[x]?x:find_set(s[x]);//递归查找 
}

void union_set(int x,int y){
    x=find_set(s[x]);
    y=find_set(s[y]);
    if(x!=y) s[x]=s[y];
} 

int main(){
    int t,n,m,x,y;
    cin>>t;
    while(t--){
        cin>>n>>m;
        init_set();
        for(int i=1;i<=m;i++){
            cin>>x>>y;
            union_set(x,y);
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            if(s[i]==i) ans++;
        }
        cout<<ans<<endl;
    }
    return 0; 
}
算法专题 文章被收录于专栏

怕忘记,好复习

全部评论

相关推荐

jack_miller:我给我们导员说我不在这里转正,可能没三方签了。导员说没事学校催的时候帮我想办法应付一下
点赞 评论 收藏
分享
点赞 1 评论
分享
牛客网
牛客企业服务