JAVA实践并查集
2016-07-24 13:09
239 查看
前言
并查集类似数学中的集合,如果是两个集合相交,那就把这两个集合合并成一个集合。社交网络中推荐可能认识的人,可能是类似原理。
二狗子与B是好友{二狗子, B}
B与A是好友{B,A}
两个相交是B
所以合并,{二狗子,B,A}
系统就可能会将B以可能认识的人给二狗子
发现了不错的文章:
参考链接:
http://blog.csdn.net/dm_vincent/article/details/7655764
http://blog.csdn.net/dm_vincent/article/details/7769159
实现功能
来及《啊哈,算法》 擒贼先擒王---并查集的应用 * 小哼的家乡出现多次抢劫事件,强盗人数众多,警察想查清楚到底有几个犯罪团伙是非常困难的,然而还是搜到了一些线索: * 10个强盗 * 1号强盗与2号强盗是同伙 * 3号强盗与4号强盗是同伙 * 5号强盗与2号强盗是同伙 * 4号-6号 * 2号-6号 * 8号-7号 * 9号-7号 * 1号-6号 * 2号-4号 * 强盗同伙的同伙也是同伙,现在需要查出有几个独立的犯罪集团。 实现检索出独立犯罪集团的个数。
中文版参考
/** * 思路来自《啊哈,算法》,我作为初学者,只是复述一遍加深印象。 * * 首先,假设10个强盗互不认识,每个人的BOSS都是自己,我们要找出每个独立犯罪集团的BOSS,及其下属 * 使用一个一维数组 r 存储 * 每个下标代表强盗编号 * 下标对应的值代表其BOSS * 例如r[2]=5 表示2号强盗的BOSS是5号强盗 * * 初始状态 * index: 1 2 3 4 5 6 7 8 9 10 * r: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} * * 然后,我们逐条线索处理。 * 此处我们假设每一对强盗中,第二个服从第一个,如说到9号-7号。则7号服从9号。 * * 1号-2号: * 令2号强盗的BOSS为1号 * r[2] = 1 // Again,每个下标代表强盗编号,下标对应的值代表其BOSS * * index: 1 2 3 4 5 6 7 8 9 10 * r: {1, 1, 3, 4, 5, 6, 7, 8, 9, 10} * * 3号-4号: * 令3号强盗的BOSS为4号 * r[3] = 4 * * index: 1 2 3 4 5 6 7 8 9 10 * r: {1, 1, 4, 4, 5, 6, 7, 8, 9, 10} * * 5号-2号: * 此处应是令2号强盗的BOSS为5号,然而 * 2号强盗的BOSS已经是1号了,如何解决? * * 方法是,既然2号强盗的BOSS是1号,那么5号去收服2号的BOSS 1号强盗 好了。 * 即r[1] = 5 * * index: 1 2 3 4 5 6 7 8 9 10 * r: {5, 1, 4, 4, 5, 6, 7, 8, 9, 10} * * -------------------------------------------------------------------------------------- * 此后,凡是碰需要收服的强盗BOSS另有其人的,都是去找最顶层的BOSS * * 例如8号需要收服9号 * 9号的BOSS是6号 * 6号的BOSS是1号 * 那么。8号直接去收服1号即可 * PPPPPS:寻找BOSS的过程是递归的,在此过程中可以将寻找到的BOSS赋值给每一个经过的下属 * 这样下次找BOSS,可以直接一步到位 * 原本的BOSS寻找: 9号→6号→1号 * 经过BOSS的替换,而可以做到:9号→1号 * 术语貌似叫压缩路径 * * 另一种情况, * r[5] = 5 * r[2] = 5 * * r[3] = 3 * r[6] = 3 * * 6号本来的BOSS是3号,但是现在他有新的BOSS 2号强盗 * 即:2号-6号,那该怎么办呢? * 答案是,让6号的BOSS 3号强盗 归顺5号强盗即可 * 即r[3] = 5 * * 总结一下并查集的思路: * 从n个集合中,寻找几个独立不相交的大集合。 * 将相交的小集合合并即可 * 合并的思路是,每次都寻找最顶层的祖先 * 例如因为B公司的业务对A公司当前有帮助(相交) * A公司收购B公司(合并) * 只需要让B公司的CEO归属A公司的CEO,不需要对每个职员都处理 * 但是每次B公司员工寻找B公司CEO时,可能需要辗转好几个上司、上司的上司等 * 此时可以优化B公司的员工跨过上司直接找到B公司的CEO。而B公司的CEO对A公司CEO负责。 * */
代码实现
public class UnionFind { public static void main(String[] args) { Scanner in = new Scanner(System.in); int[] robber = new int[11]; init(robber); showArray(robber); int employer, employee; for (int i = 1; i < robber.length - 1; i++) { employer = in.nextInt(); employee = in.nextInt(); unionRobber(robber, employer, employee); } showArray(robber); int sum = 0; for (int i = 1; i < robber.length; i++) { if (robber[i] == i) { sum++; } } System.out.println(sum); } public static void init(int[] robber) { for (int i = 1; i < robber.length; i++) { robber[i] = i; } } public static void showArray(int[] robber) { for (int i = 1; i < robber.length; i++) { System.out.print(robber[i] + " "); } System.out.println(); } public static void unionRobber(int[] robber, int employer, int employee) { //传进来的是小盗贼头目,可能有大BOSS呢,寻找他 int t1 = findBigBoss(robber, employer); //可能有上司,让上司对大BOSS负责 int t2 = findBigBoss(robber, employee); //如果我们找到了小盗贼的BOSS //同时找到了小盗贼头目的BOSS //并且不是同伙的,那么让小盗贼的BOSS纳入麾下 if (t1 != t2) { robber[t2] = t1; } } public static int findBigBoss(int[] robber, int pos) { //如果他的BOSS是自己,不就是大BOSS嘛 if (robber[pos] == pos) { return pos; } else { //寻找他的上级BOSS,寻找过程中压缩路径。即让所有下属直接对顶层BOSS负责 robber[pos] = findBigBoss(robber, robber[pos]); return robber[pos]; } } }
结果
输出: 1 2 3 4 5 6 7 8 9 10 输入: 1 2 3 4 5 2 4 6 2 6 8 7 9 7 1 6 2 4 输出: 5 5 5 5 5 5 8 9 9 10 3
结尾一点
首先一开始看的时候还是有点糊涂的,然后就搜到了开头两篇文章,在了解了应用范围之后,忽然就通了。大概这是抽象能力不足的表现。
复述的过程中,会感觉到对其思想产生了解,写代码能从整体考虑。
首先我要能找任意盗贼的上司
其次我要能把找到的上司告诉那个盗贼,如果不是同一个上司的话
然后就完了。
END
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树