并查集

来源:互联网 发布:公众号助手电脑版 mac 编辑:程序博客网 时间:2024/06/06 19:45

并查集(Union-find set),从字面上来看就是支持合并(union),查找(find) 的集合嘛。下面我们来学习一下这种数据结构

 并查集的精髓(即它的三种操作,结合实现代码模板进行理解):

1、Make_Set(x) 把每一个元素初始化为一个集合

初始化后每一个元素的父亲节点是它本身,每一个元素的祖先节点也是它本身(也可以根据情况而变)。

2、Find_Set(x) 查找一个元素所在的集合

查找一个元素所在的集合,其精髓是找到这个元素所在集合的祖先!这个才是并查集判断和合并的最终依据。
判断两个元素是否属于同一集合,只要看他们所在集合的祖先是否相同即可。
合并两个集合,也是使一个集合的祖先成为另一个集合的祖先,具体见示意图

3、Union(x,y) 合并x,y所在的两个集合

合并两个不相交集合操作很简单:
利用Find_Set找到其中两个集合的祖先,将一个集合的祖先指向另一个集合的祖先。如图

Make_Set(x)就不多解释了,直接看代码

for(int i=1;i<=n;i++)

parent[i]=i;

下面看看并查集的核心Find_Set(x),其实这个操作并不只是简单的查找祖先的操作还有路径压缩的操作,以下图为例


假设我们在查找a的祖先,找到了d,而b,c,的祖先都是d,但是我们可能下一次要查找b的祖先,但是依然要从b往上面递归,显然次数多了,时间就多了,其实我们在之前查找a的祖先的时候,已经知道了b的祖先是d,这时候便有了我们的路径压缩操作。

看一下查找的一些操作

这是朴素查找的代码,适合数据量不大的情况:int findx(int x){int r=x;while(parent[r] !=r)r=parent[r];return r;}下面是采用路径压缩的方法查找元素:int find(int x) //查找x元素所在的集合,回溯时压缩路径{if (x != parent[x]){parent[x] = find(parent[x]); //回溯时的压缩路径} //从x结点搜索到祖先结点所经过的结点都指向该祖先结点return parent[x];}上面是一采用递归的方式压缩路径, 但是,递归压缩路径可能会造成溢出栈,下面我们说一下非递归方式进行的路径压缩:int find(int x){int   r,tem;r = x;while(r != parent[r]) //查找跟节点r = parent[r]; //找到跟节点,用r记录下 while(x != r) //非递归路径压缩操作{    tem=parent[x];  parent[x]=r;    x=tem;  }return x; //返回根节点的值 }
最后看看合并的操作



void Unionset(int x,int y){    int x1 = Find(x),y1 = Find(y);    if(x1>y1) parent[x1] = y1;    else parent[y1] = x1;}




原创粉丝点击