JAVA实践并查集

前言

并查集类似数学中的集合,如果是两个集合相交,那就把这两个集合合并成一个集合。
社交网络中推荐可能认识的人,可能是类似原理。
二狗子与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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值