一、并查集:
复杂度为α(n)
并查集是一种树形结构,处理一些互不相交的集合之间的合并及查询问题,支持查找和合并。
1.初始化:
每个节点单独属于一个集合。
void init(){
for(int i=1;i<=n;i++) fa[i]=i;
return;
}
2.查找:
int find(int x){
if(fa[x]==x) return x;
else return find(fa[x]);//树的结构固定,未做任何修改
}
路径压缩:
把每个点都直接连到祖先上去,因为只在意是否是同一个集合,即只关心祖先是谁,而并不关心父亲是谁。
在搜索回溯的时候进行。
int find(int x){
if(x!=fa[x]) fa[x]=find(fa[x]);
//树更改
return fa[x];
}
3.合并:
合并后谁为祖先无所谓。
void unionn(int x,int y){
x=find(x); y=find(y);
fa[x]=y;
}
按秩合并:
记录集合大小,每次合并时小的集合合并到大的集合里。
void unionn(int x,int y){
x=find(x); y=find(x);
if(x==y) return;
if(size[x]>size[y]) swap(x,y);
fa[x]=y;
size[y]+=size[x];
}
【例题】通畅交通
(http://acm.hdu.edu.cn/showproblem.php?pid=1232)
只需要图中有几个连通块,连通块数-1即为需要的道路。
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=1005;
int n,m,ans;
int f[N];
int find(int x){
return f[x]==x?x:f[x]=find(f[x]);
}
void unionn(int x,int y){
x=find(x); y=find(y);
f[x]=y;
}
int main(){
while(~scanf("%d",&n)){
if(n==0) break;
ans=0;
scanf("%d",&m);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
unionn