恩,前两周学习了并查集,是时候总结一下了。
等价关系与等价类
从数学上看,等价类是一个对象(或成员)的集合,在此集合中的所有对象应满足等价关系。若用符号"≡"表示集合上的等价关系,那么对于该集合中的任意对象x,y, z,下列性质成立:
1、自反性:x ≡ x
2、对称性:若 x ≡ y 则 y ≡ x
3、传递性:若 x ≡ y 且 y ≡ z 则 x ≡ z
因此,等价关系是集合上的一个自反、对称、传递的关系。
通过金属线连接起来的电器的连通性,就是一种等价关系。这种关系显然具有自反性,因为任何一个器件都是与自身连通的;如果a 电连通b,那么b一定也电连通a,因此这种关系具有对称性; 若a连通到b,并且b连通到c,那么a连通到c 。
并查集
并查集的一般用途就是用来维护某种具有自反、对称、传递性质的关系的等价类。并查集一般以树形结构存储,多棵树构成一个森林,每棵树构成一个集合,树中的每个节点就是该集合的元素,找一个代表元素作为该树(集合)的祖先。
并查集支持以下三种操作:
1、Make_Set(x) 把每一个元素初始化为一个集合
初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身。
int father[1200],rank[1200],sum; void makeset() { int i; for(i=0; i<1200; i++) { father[i]=i; rank[i]=1; } }
2、Find_Set(x) 查找一个元素所在的集合
查找一个元素所在的集合,只要找到这个元素所在集合的祖先即可。判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。
int find(int x) { return father[x] != x ? father[x]=find(father[x]) : x;//状态压缩、 }
3、Union(x,y) 合并x,y所在的两个集合
合并两个不相交集合操作很简单:首先设置一个数组Father[x],表示x的"父亲"的编号。那么,合并两个不相交集合的方法就是,找到其中一个集合的祖先,将另外一个集合的祖先指向它。
void Union(int x,int y) { x=find(x); y=find(y); if(x==y) return; if(rank[x]>rank[y]) { father[y]=x; rank[x]+=rank[y]; } else { father[x]=y; rank[y]+=rank[x]; } }
并查集的优化
1、find(x)时 路径压缩
寻找祖先时我们一般采用递归查找,但是当元素很多亦或是整棵树变为一条链时,每次Find_Set(x)都是O(n)的复杂度,有没有办法减小这个复杂度呢?
答案是肯定的,这就是路径压缩,即当我们经过"递推"找到祖先节点后,"回归"的时候顺便将它的子孙节点都直接指向祖先,这样以后再次Find_Set(x)时复杂度就变成O(1)了。
2、Union(x,y)时 按秩合并
即合并的时候将元素少的集合合并到元素多的集合中,这样合并之后树的高度会相对较小。
一道并查集的模板题目:NYOJ608畅通工程
4 2 1 3 4 3 3 3 1 2 1 3 2 3 5 2 1 2 3 5 999 0 0
1 0 2 998
#include <cstdio> int father[1200],runk[1200]; void makeset() //初始化 { for(int i=0;i<1200;i++){ father[i]=i;runk[i]=1; } } int find(int x) //查找+状态压缩 { return father[x] != x ? father[x]=find(father[x]) : x;//状态压缩、 } int Union(int x,int y,int ans) //合并 { x=find(x); y=find(y); if(x==y) return ans; else if(runk[x]>runk[y]) { father[y]=x; runk[x]+=runk[y]; ans++; } else{ father[x]=y; runk[y]+=runk[x]; ans++; } return ans; } int main() { int num_town,num_road,a,b; while(~scanf("%d",&num_town)&&num_town) { scanf("%d",&num_road); int ans=0; makeset(); while(num_road--) { scanf("%d%d",&a,&b); ans=Union(a,b,ans); } printf("%d\n",num_town-1-ans); } return 0; }