浅谈 并查集之模板

并查集 - 模板

并查集,顾名思义,就是能 并(合并)查(查询)集(集合)

需求

  1. 合并集合 (一般会给你两个点,需要合并两点所在的集合)
  2. 询问两点是否在同一集合

实现

思想

并查集是将某些点(可以为一个点)看作一个集合,然后有若干个集合。
因为我们只是要合并集合以及询问两点是否在同一集合之内,所以集合内点的顺序是不需要关心的。
也就是说,在一个集合的点内随便找出一个点作为(root),其余点皆是他的儿子,这样就可以将每一个集合储存起来并为下文做好铺垫

合并

合并极其简单,只需要将两个集合中的一个集合的(root)作为新集合的(root),另一个集合的(root)则作为新集合(root)的儿子。

Code
//......

for(int i=1;i<=n;++i) f[i]=i;
// 初始化f数组,f[i]记录i的父亲
// 至于初始化的值,只要你能知道这个值代表ta没有爸爸而且不会与数据冲突就好

//......

int find(int x){
	if(x==f[x]) return x; // ta没有父亲,所以ta是这个集合的root
	return find(f[x]); // 接着寻找ta的爸爸所在集合的root (与寻找x所在集合的root等价)
}
//这个find()为查找节点x所在集合的root

//......

f[find(x)]=find(y);
//合并,找到两点所在的集合,让其中的一个root作为另一个root的儿子

//......

询问

询问依旧极其简单,只需要判断两点所在集合的(root)是否相等,相等则在一个集合内,不相等则在不同的集合内。

Code
//......

if(find(x)==find(y)) // 相同,在同一集合内
    puts("Y");
else // 不相同,不在同一集合内
    puts("N");
// 判断两点各所在集合的root是否相同

//......

亿些优化

  1. 在查询的时候,若集合内节点(设节点个数为(n))形成一条链,则查询叶子节点的复杂度为(O(n)),那么多次查找则复杂度会爆炸。
    这时候,我们就需要一些优化,显然,我们可以压缩路径,在我们查询一个点((x))时,当找到这个集合的(root)的时候,我们可以直接把(x)作为(root)的儿子,这样再查询(x)节点的时候,我们就可以使用(O(1))的复杂度来解决了。
   //---Code---
   //......
   
   int find(int x){
       if(x==f[x]) return x;
       return (f[x]=find(f[x])); // 这里使用了高(qi)超(ji)技(yin)艺(qiao),将x的爸爸改为ta所处集合的root,下次查询效率将提升
   }
   
   //......
  1. 我不会了......

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
using namespace std;
#define ull unsigned ll
#define INF 0x3f3f3f3f
#define ll long long
#define GC getchar()
#define R read()
ll read(){
    char c=GC; ll s=0,f=1;
    while(c<'0'||c>'9'){if(c=='-')f=-f;c=GC;}
    while(c>='0'&&c<='9'){s=s*10+c-'0';c=GC;}
    return s*f;
}
int n,m;
int op,x,y; 
int f[10005];
int find(int x){ //查询其所在集合的root
	if(x==f[x]) return x;
	return (f[x]=find(f[x]));
}
int main(){
	n=R; m=R;
	for(int i=1;i<=n;++i) f[i]=i; // 初始化
	for(int i=1;i<=m;++i){
		op=R; x=R; y=R;
		if(op==1) f[find(x)]=find(y); // 合并
		else if(op==2) if(find(x)==find(y)) puts("Y"); else puts("N");  //询问
	}
    return 0;
}
原文地址:https://www.cnblogs.com/FUXyao/p/14016896.html