并查集及其实现

本文介绍了并查集的基本概念,包括其在维护大量集合中的高效操作(isSameSet和union),以及如何通过HashMap实现,重点讲解了代表节点的概念、路径压缩和集合合并的过程。
摘要由CSDN通过智能技术生成

1.什么是并查集

假设一开始有几个样本,a,b,c,d,e,f 一开始,我们认为他们所在的集合只有自己{a},{b},{c},{d},{e},{f},并查集他是维持着一大堆集合的结构。

并查集提供两个操作:

1.isSameSet   查询两个样本是否在同一个集合  返回Boolean类型  例如 isSameSet(a,e)判断a所在的集合和e所在的集合是不是同一个集合

2.union  将两个样本所在集合的全体,变成一个集合  例如 union(a,e)判断将a所在的集合和e所在的集合合成一个集合。

如果有N个样本,我们调用isSameSet和union很频繁,他能够做到均摊下来单次时间复杂度O(1)。假如说我100万个样本,我们的isSameSet和Union操作巨频繁,频繁到超过100万次,单次平均时间复杂度O(1)。简而言之就是当我们的查询次数大于样本量,那么单次查询时间复杂度O(1)。

2.实现

2.1  什么是代表节点?

从一个集合的任意一个值开始,我们一直往上找,知道不能再往上了,那么这个节点,就是该集合的代表节点。

2.2  isSameSet

我们可以把每个样本都包上一层,让他们有一个指针,指向自己。假如说我判断a  e 是否在一个集合,我就找a的代表节点和e的代表节点,看是不是同一个,如果是,则说明在同一个集合,如果不是,则证明不是同一个集合。

2.3  union

如果我们要union a 和 e ,我们先找a的代表节点,再找e的代表节点,然后e的代表节点的指针直接指向a。如果两个节点所在的集合的大小不一样,我们找到相关代表节点后,把小的集合挂在大的集合上面。

2.4 并查集使用HashMap实现
public static class Node<T>{
        private T value;

        public Node(T value){
            this.value = value;
        }
    }

    public static class UnionSetWithHashMap<T>{
        // T 是样本
        // Node<T> 是包完后那个圈
        //对于使用者来说,他是不知道那个圈的,他就人它的样本
        //但我们操作的是那个圈,所以需要把他们对应的一一记录下来
        private HashMap<T,Node<T>> nodes;
        //因为我们没有在node上面添加指针,所以,我们用一张表,来实现指针的效果
        //key为带圈的数据, value 为它的父亲
        private HashMap<Node<T>,Node<T>> parents;
        //我们连接是,需要把小的连接到大的上面,所以我们就需要把代表节点所在集合的大小保存起来
        //key为代表节点, value为代表节点所在集合的大小
        //只有代表节点会存在这个集合中,如果不是代表节点,就会从这个集合中删除
        private HashMap <Node<T>,Integer> sizeMap;

        public UnionSetWithHashMap(List<T> value){
            parents = new HashMap<>();
            sizeMap = new HashMap<>();
            for (T t : value) {
                Node<T> node = new Node(t);
                //初始化,将给的每一个值,都包一层,相当于都加一个圈
                nodes.put(t,node);
                //初始化时,每个节点的代表节点都是他自己,大小都是1
                parents.put(node,node);
                sizeMap.put(node,1);
            }
        }

        //寻找到该节点的代表节点
        public  Node<T> findFather(Node<T> node){
            //准备一个栈,用来做路径压缩
            Stack<Node<T>> stack = new Stack<>();
            while (node != parents.get(node)){
                node = parents.get(node);
                //将经过的节点全都放入栈中
                stack.push(node);
            }
            //一个一个弹出,把他们的父亲变为 代表节点
            /**        a               假如说 e 找代表节点,在找的过程中进行路径压缩
             *        / \               等找到代表节点后,就会变成
             *       b   c                         a
             *      /                             / \
             *     d                           [b,c,d,e]
             *    /                          b c d e  均为 a 的孩子,相当于一个多叉树
             *   e
             */
            while (!stack.isEmpty()){
                Node<T> pop = stack.pop();
                parents.put(pop,node);
            }
            return node;
        }

        public Boolean isSameSet(T a,T b){
            return findFather(nodes.get(a)) == findFather(nodes.get(b));
        }

        public void union(T a,T b){

            Node<T> aHead = findFather(nodes.get(a));
            Node<T> bHead = findFather(nodes.get(b));
            //当代表节点不是同一个的时候需要合并
            if (aHead != bHead){
                Integer aSize = sizeMap.get(aHead);
                Integer bSize = sizeMap.get(bHead);
                //找出谁的集合大
                Node<T> bigHead = aSize >= bSize ? aHead : bHead;
                Node<T> smallHead = bigHead == aHead ? bHead : aHead;

                //将集合小的父亲设置为大的集合
                parents.put(smallHead,bigHead);
                //将小的集合从代表节点表移除,大的代表节点大小需要加上小的集合的大小,就是合并后的集合的大小
                sizeMap.put(bigHead,aSize + bSize);
                sizeMap.remove(smallHead);
            }
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值