这篇文章主要介绍了C++如何实现并查集算法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
并查集是用一个数组实现的。首先先定义一个数组:
int father[N];
father[i]表示元素i的父亲结点。
接下来进行初始化。一开始,每个元素都分别是独立的一个集合,父亲结点就是它自己,所以初始化时将所有father[i]等于i:
for(int i = 1; i <= N; i++){ father[i] = i; }
这样,就将father数组初始化完毕。
由于规定同一个集合中只存在一个根结点,因此查找操作,就是查找给定结点的根结点的过程。可以通过递推或递归来实现,思路都是一样的,都是反复寻找父亲结点,直到找到根结点为止。
递推代码:
//findFather函数返回元素x所在集合的根结点 int findFather(int x){ while(x != father[x]){ //如果不是根结点,继续循环 x = father[x]; //获得自己的父亲结点 } return x; }
上述代码中, while(x != father[x]),说明当x的父亲结点不等于本身时,也就是x不是根结点时就继续循环,因为父亲结点等于本身这个情况,只有在根结点才会出现。
递归代码:
int findFather(int x){ if(x == father[x]) return x; //如找到根结点,就返回根结点编号x else return findFather(father[x]); //否则,递归判断x的父亲结点是否是根结点 }
合并,就是把两个集合合并成一个集合。实现过程是:先判断两个元素是否属于同一个集合,不属于同一个集合,就开始进行合并操作。判断两个元素是否属于同一个集合的具体思路,就是调用上面的findFather函数,分别查找两个元素所属集合的根结点,根结点不同,则两个元素不属于同一个集合。合并两个集合的具体思路,就是将其中一个集合的根结点的父亲指向另外一个集合的根结点即可。
合并操作的代码实现:(假设有两个集合,一个集合里有元素a,一个集合有元素b)
void Union(int a, int b){ //让一个集合的根结点的父亲指向另一个集合的根结点 father(findFather(a)) = findFather(b); }
注意,合并操作之前,最好先判断下待合并的两个元素是否位于同一个集合。
由于findFather函数目的就是查找根结点,所以,我们在查找结点的路径上直接将所有结点的父亲都指向根结点,查找的时候就不必一直回溯去寻找父亲了,查询的复杂度可以降为O(1)。
比如下面这张图:
观察图不难发现,上图中father[1] = 1,father[2] = 1,father[3] = 2,father[4] = 3。经过路径压缩,就变成下面这幅图:
相当于将所有结点的父亲都直接指向根结点,这就是路径压缩。
如何用代码实现路径压缩呢?以下是具体代码:
int findFather(int x){ if(father[x] != x) father[x] = findFather(father[x]); return father[x]; }
以上代码,实现了在查询获取根结点的同时,将路径进行压缩优化,代码虽然很短,但是很巧妙,下面解释下上述代码:
if(father[x] != x),当所查找的元素x的父亲结点不是自己,也就是x不是根结点时,
findFather(father[x]),就继续递归查找父结点,直到找到根结点为止,
father[x] = findFather(father[x]),然后将找到的根结点直接赋给x的父亲结点。
这样就实现了路径压缩,即将结点的父亲直接指向根结点。
return father[x],返回查找到的根结点。
感谢你能够认真阅读完这篇文章,希望小编分享的“C++如何实现并查集算法”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。