并查集实例详解
来源:互联网 发布:卓智网络是国企 编辑:程序博客网 时间:2024/06/06 00:17
一、简介
并查集,是一种很有用的数据结构。在算法导论的第22章:用于不相交集合的数据讲的就是并查集,看算法导论确实是需要耐心和耐心的。大致过了一遍,这篇博文将结合自己的理解,举个生动的例子,并用代码实现,让学习并查集变得有趣。
多个不相交的数据集合可以把它想象为多棵独立的树。
并查集的常用基本操作有
- 查找任意两个树节点,看看它们是否属于同一棵树(其实就是查不同节点的根节点是否一样即可),如下图b & e 同属于根为c的树,所以它们在同一个集合;而h & g则不属于同一集合。
- 合并这些独立的树,成为一棵更庞大的树。(其实也很简单,就是将一颗树的根节点成为另一个树根节点之下的孩子即可)
合并操作如图所示:
1和2两步,也就是所谓的查与并。
二、数据结构描述
刚开始我听这高大上的数据结构名感觉应该会很难,又是树又是森林什么的。然而研究了一番发现并查集并不是想象中那么难,而且特别有意思。
举个例子,假设有A B两家公司。 (画图是两棵树,语言描述如下)
A:
*********6 ——–老板
******2****3 ——经理
******5****4 ——-员工 (5号员工的直接上司是2号经理,其余类似)
B:
********8 —– ——老板
*****1******7 ——经理 (7号经理手下无人)
*****9 —————-部长
*****0 —————员工
并查集的数据结构表示非常简单直观,用数组表示即可。
那么用一个数组就可以表示两家公司的等级构成:
int myboss[ 10 ] = { 9, 8, 6, 6, 3, 2, 6, 8, 8, 1 };
即0号员工的上司是9号,1号员工的上司是8号,2号员工的上司是6号……
这么简单的一个数组就表示了两棵树,一个森林,有点意思。
那么如何实现并 & 查便是并查集的关键之处。
三、并查集之并与查
1-查的两种实现
1)非递归方式
查老板,首先要明确一点就是,老板的老板就是自己!开公司就是自己给自己当老板,多霸气。
假如我们想查5号员工的老板是谁?应该怎么查呢?
思路很简单,先问5号的上司2号,问问他是不是老板,他说我不是老板,我只是普通的经理而已。然后2号经理说,我帮你向上级问问吧。于是2号经理打电话给自己的上司6号,问6号是不是老板。6号说废话,我就是你老板(因为6号没有上司了)。
通过逐层向上询问的方式,我们就可以查找到任意员工的最终老板是谁。(也就是从任意节点回溯到树的根节点)
如何用代码实现以上的思路呢?
只需以下这个简单的函数即可:
//不断向上级询问找某人的老板//老板的老板就是自己int find_boss(int person) { int boss = person; //先假设该员工就是老板 int leader,tmp; //tmp用来保存某员工的直接上司 //没找到老板则一直循环 while ( myboss[boss] != boss ) boss = myboss[boss]; //*******路径优化******// //每一个人的直接上司都变成老板(不再是之前的经理或部长了) leader = person; while (leader != boss) { tmp = myboss[leader]; myboss[leader] = boss; //上司直接变成老板 leader = tmp; } return boss;}
路径优化
上面的代码有一段是所谓的路径优化。什么意思呢?
其实很简单,就是原来的那颗三层的树,变得只剩下两层变成
如下形状:
******6 ——老板
2***3****4****5 —–经理
也就是说4号和5号员工都升职加薪,走向人生小巅峰了,老板成为了他们的直接上司(然而,他们手下并没有员工,光杆司令)。
而什么时候会发生路径优化呢?就是每查一次,并查集的层次结构就会改变一点。即,只有打电话问谁是老板的员工才能获得机会。
例如执行了 find_boss(5);
那么公司结构变为:
**********6 ————老板
******2**5***3 —-经理(5号升职了)
**************4 —-员工(4号仍然是3号的员工,因为他没有主动打电话给老板)
假如B公司的9号员工打给老板要求升职
那么公司结构则变为:
***********8
********1**7***9
****************0
2)递归方式
递归方式的查老板那就更加形象简单了,路径优化这一过程多省略了,因为在递归回溯的时候就完成了路径优化,升职加薪了。
代码:
//递归方式找老板,在递归回溯的过程中同时也实现了路径优化//即每个人的直接上司都变成老板了 r=recursiveint find_boss_r(int person) { if( person != myboss[person]) myboss[person] = find_boss_r(myboss[person]); return myboss[person];}
2.A,B公司合并
并查集另一个重要的操作就是并了。上述例子的A,B两家公司合并就是并查集的并操作。
那么如何合并呢?十分简单形象,加入是A公司收购了B公司,那么只需要让B公司的老板,变为A公司老板的直接下属就可以了。
具体实现代码:
//合并两家公司,即A公司老板成为了B公司老板的老板void join(int person_one, int person_two) { int boss_a ,boss_b; boss_a = find_boss(person_one); boss_b = find_boss(person_two); if (boss_a != boss_b) myboss[boss_b] = boss_a;}
四、测试代码:
int main(int argc,char *argv[]){ int boss; boss = find_boss(9); printf("9号员工的老板是:%d \n",boss); boss = find_boss_r(4); printf("4号员工的老板是:%d \n",boss); join(5,0); boss = find_boss(7); printf("A公司收购B公司后,7号的老板是: %d\n",boss); return 0;}
五、并查集的应用
理解了并查集可以解决一大类相似的问题,不过具体如何解决还是得看个人功底了。
大家百度一下就有很多难题,在此就省略了。
- 并查集实例详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- 并查集详解
- UISegmentControl
- spwuezjxij
- 如何初始化二维数组
- Android应用架构
- Linux小知识
- 并查集实例详解
- epoll使用详解(精髓)
- 面试备用
- volley学习笔记(六):volley框架下发送和读取cookie
- 计算机英语学习(二)
- Google C++ Coding Style:右值引用(Rvalue Reference)
- JDOM解析XML
- Lubuntu 12.04开机启动应用程序脚本
- Leetcode 162 Find Peak Element 查找峰值元素(极大值)