所谓并查集,顾名思义,是一个支持“并”,“查”的集合。
并查集可以想象成一个森林,就是树的集合。
在并查集中,还有一个重要思想:代元法。
其实就是在每一个集合中选一个固定元素,作为集合的代表。
有一种定义方法:用树形数据结构存储数据,树上的每一个节点都是一个元素,树根是代表元素。
但一般来说,我们会维护一个fa数组,fa来保存父节点,根节点的fa是他自己。
在我们初始化一个并查集时,元素的fa都是自己,然后才有get(查找)和merge(合并)操作。
建并查集:
1 int fa[10001]; 2 void init() 3 { 4 for(int i=1;i<=10001;i++) 5 fa[i]=i; 6 }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
路径压缩
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
路径压缩 的思想是,我们只关心每个结点的父结点,而并不太关心树的真正的结构。这样我们在一次查询的时候,可以把查询路径上的所有结点的 father[i]father[i] 都赋值成为根结点。只需要在我们之前的查询函数上面进行很小的改动。
比如说一棵存并查集的树
这就是它的路径压缩,大大的减小了查询复杂度。
接下来我们就来写Get,Merge的模板。
查询(Get)
1 int Get(int x) 2 { 3 if(fa[x]==x)//是树根就返回自己 4 return x; 5 return fa[x]=Get(fa[x]);//递归查找父亲 6 }
合并(Merge)
1 void Merge(int x,int y) 2 { 3 fa[Get(x)]=Get(y);//x的树根变成y的树根 4 }
其实除了并与查之外,还考一个操作:判断两个数是否属于同一集合。
这个就直接用Get函数查找父亲是否相同就可以了。
1 bool Judge(int x,int y) 2 { 3 int a=Get(x),y=Get(y); 4 return a==b; 5 }
其实,这几个模板操作还很容易理解,重在多练,多想。
之后我会再讲解一些习题,来帮助大家深入理解并查集。
我们下次见!