并查集那最简单的应用就是合并两个元素和查询两个元素是否属于同一集体,它的工作过程如下:首先将每个元素放在单独的一个集合里,集合的名字就是这个元素的编号,对于查询操作就找两个元素的所在集合编号,集合编号一样的就是一个集合里的元素,反之则不是。那么普通并查集是如何合并使得原本不同集合的元素隶属同一个集合那?当合并的时候把其中一个元素的从属集合设为另一个元素,我们用树里儿子父亲的关系表示,这时父子两个就是一个集合里的了,当查询一个元素的时候我们就顺着族谱往上找,直到找到那个他的集合编号就是他自己的编号的元素,也就是原始祖先,这样就可以判断两个元素是否隶属同一个集合。但是这样就会产生一个问题就是并查集是随机并的,最后可能产生那种一条龙一样的树,使得查询的复杂度很高。
这就引出了路径压缩,当查询到一个元素的所在集合时候,就把这个元素的父亲设为原始祖先,同样的这个元素往上的爸爸们的父亲也设成原始祖先,这样就可以在下一次查询时使得复杂度变低很多,而路径压缩只是一个往上遍历时顺便的过程,不会增加复杂度,具体做法是在向上查询时把原始祖先的返回值赋给下面的后代们,使他们都变成儿子。
模板:
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 template<typename T>void read(T &a) 5 { 6 int f=1,x=0;char ch=getchar(); 7 while(!isdigit(ch)) 8 { 9 if(ch=='-')f=0;ch=getchar(); 10 } 11 while(isdigit(ch)) 12 { 13 x=(x<<1)+(x<<3)+ch-'0';ch=getchar(); 14 } 15 a=f?x:-x; 16 } 17 const int maxn=10008; 18 int n,m; 19 int opt,x,y; 20 int fa[maxn]; 21 int fx,fy; 22 int find(int x) 23 { 24 return fa[x]==x?x:fa[x]=find(fa[x]);//对fa的赋值就是路径压缩 25 } 26 int main() 27 { 28 read(n),read(m); 29 for(int i=1;i<=n;++i)fa[i]=i; 30 while(m--) 31 { 32 read(opt),read(x),read(y); 33 if(opt==1) 34 { 35 fx=find(x),fy=find(y); 36 fa[fx]=fa[fy]; 37 }else{ 38 fx=find(x),fy=find(y); 39 if(fx==fy)printf("Y "); 40 else printf("N "); 41 } 42 } 43 return 0; 44 }