并查集

并查集

 并查集要解决的问题就是集合类型的问题,就是对于集合的并和查。并就是将两个集合合并成一个新的集合,查就是查两个个元素是否在同一个集合中。
 因而其提供给用户的接口也应该为两个,一个isSameSet()方法用于查,一个union()方法用于并。
 显然,如果是用链表来实现的话,并是一个O(1)的操作,但是查将会是O(n)的操作;而如果用哈希表来实现的话,查是一个O(1)的操作,但是并将会是O(n)的操作。因而,通常情况下使用的是类似链表+哈希表的形式。
 原理就是设置头元素来表示一个集合的起始位置,初始情况下,每个元素就是自己集合的头元素。然后集合中所有元素都会有向上指的指针,最终都是会指到集合头元素处,类似于一个个的单链表,这里使用的是一个fatherMap的hash表来实现类似链表的形式,用于存放节点与节点父亲的对应关系。

 下面是并查集代码的实现,这种使用HashMap的形式适用于各种数据类型。

public class UnionFindSet_code {
    public static class UnionFindSet {
        public HashMap<String, String> fatherMap;
        public HashMap<String, Integer> sizeMap;

        public UnionFindSet(List<String> list) {
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();

            //初始化并查集
            for (String str : list) {
                fatherMap.put(str, str);
                sizeMap.put(str, 1);
            }
        }

        private String findHead(String s) {
            //先判读是否是并查集中的元素
            if (fatherMap.containsKey(s)) {
                String temp = s;
                //从该元素一直往上走,直到找到父亲为自己的元素,这就是头元素
                while (!temp.equals(fatherMap.get(temp))) {
                    temp = fatherMap.get(temp);
                }
                return temp;
            }
            return "";
        }

        public boolean isSameSet(String a, String b) {
            //判断是否是并查集中的元素,不是的话直接返回false
            if (fatherMap.containsKey(a) && fatherMap.containsKey(b)) {
                //如果两个元素的头为同一个的话,说明它们属于同一个集合
                if (findHead(a).equals(findHead(b))) {
                    return true;
                }
            }
            return false;
        }

        public void union(String a, String b) {
            //判断是否是并查集中的元素
            if (fatherMap.containsKey(a) && fatherMap.containsKey(b)) {
                //判断是否是同一个集合中的元素,如果不是才进行合并
                if (!isSameSet(a, b)) {
                    String aHead = findHead(a);
                    String bHead = findHead(b);
                    String big = sizeMap.get(aHead) > sizeMap.get(bHead) ? aHead : bHead;
                    String small = aHead == big ? bHead : aHead;
                    //将元素少的集合合并到元素多的集合中去,也就是将元素少集合的头的父亲设为元素多的集合的头
                    fatherMap.put(small, big);
                    //还要将集合元素数量更新
                    sizeMap.put(big, sizeMap.get(big) + sizeMap.get(small));
                    sizeMap.remove(small);
                }
            }
        }
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("java");
        list.add("python");
        list.add("c++");
        list.add("hadoop");
        list.add("hbase");
        list.add("mysql");
        list.add("redis");
        list.add("flink");
        list.add("spark");

        UnionFindSet unionFindSet = new UnionFindSet(list);
        unionFindSet.union("java", "python");
        unionFindSet.union("python", "c++");
        unionFindSet.union("hadoop", "hbase");
        unionFindSet.union("redis", "mysql");
        System.out.println(unionFindSet.isSameSet("java", "c++"));
        System.out.println(unionFindSet.isSameSet("java", "hadoop"));
        System.out.println(unionFindSet.isSameSet("flink", "spark"));
        System.out.println(unionFindSet.isSameSet("redis", "mysql"));
    }
}

在这里插入图片描述
在这里插入图片描述
 执行程序后,结果所得如下,与图片情况一致。
在这里插入图片描述

 在做题过程中,总是会遇到对正整数使用并查集的情况,这里就可以不使用HashMap,而是使用数组形式,更加方便一些。代码如下:

public class UnionFindSet_Integer {
    public static class UnionFindSet {
        public int[] father;
        public int[] size;

        public UnionFindSet(List<Integer> list, int max) {
            father = new int[max + 1];
            size = new int[max + 1];
            for (Integer i : list) {
                father[i] = i;
                size[i] = 1;
            }
        }

        private int findHead(int a) {
            while (a != father[a]) {
                a = father[a];
            }
            return a;
        }

        public boolean isSameSet(int a, int b) {
            if (findHead(a) == findHead(b)) {
                return true;
            }
            return false;
        }

        public void union(int a, int b) {
            if (!isSameSet(a, b)) {
                int aHead = findHead(a);
                int bHead = findHead(b);
                int big = size[aHead] > size[bHead] ? aHead : bHead;
                int small = big == aHead ? bHead : aHead;
                father[small] = big;
                size[big] = size[big] + size[small];
                size[small] = 0;
            }
        }
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i);
        }
        int max = list.get(list.size() - 1);
        UnionFindSet unionFindSet = new UnionFindSet(list, max);
        unionFindSet.union(1, 3);
        unionFindSet.union(3, 5);
        unionFindSet.union(5, 7);
        unionFindSet.union(1, 9);
        unionFindSet.union(2, 4);
        unionFindSet.union(2, 6);
        unionFindSet.union(2, 8);
        System.out.println(unionFindSet.isSameSet(1, 7));
        System.out.println(unionFindSet.isSameSet(1, 6));
        System.out.println(unionFindSet.isSameSet(4, 8));
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

loser与你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值