• PriorityQueue及二叉堆


    PriorityQueue是一个优先级队列,底层是小顶堆实现

    概念

    • 优先级队列

    通常的队列是先进先出,那有一种特殊的队列并不是先进先出,而是根据优先级的顺序出队

    • 二叉堆

    二叉堆是一种数据结构,堆是一种特殊的二叉树,满足一下条件的二叉树

    1.该二叉树必须是一个完全二叉树。

    2.子节点的值总是单调的。这里又分为两种情况,如果子节点总是小于等于父节点,那么整体的树顶元素就越大,那么我们叫它大顶堆,反过来子节点总是大于等于父节点,那么我们叫它小顶堆

    • 完全二叉树

    元素是按照从上到下层级,从左到右的顺序排列的树形结构

    上面我们说了二叉堆,那么其实堆也可以是多叉堆,多叉堆同理也有上面两个类似性质。1.完全多叉树 2.父子节点单调性

    二叉堆

    虽说这里的数据结构是二叉树,但实际装数据的容器,或者说底层还是使用的数组,PriorityQueue中源码:

    transient Object[] queue; // non-private to simplify nested class access
    

    在上图中,我们给每个元素的下标做了标注,足够细心的你会发现,数组下标,存在以下关系:

    leftNo = parentNo * 2 + 1
    rightNo = parentNo * 2 + 2
    parentNo = (currentNo -1) / 2
    

    这样一来,我们在得到任意节点的情况下,就能通过该公式找到它的父节点和子节点

    入堆

    当有了基础的概念和理论之后,我们来构建堆,像堆中加入元素就是构建堆的一个过程。

    
     /**
         * Inserts the specified element into this priority queue.
         *
         * @return {@code true} (as specified by {@link Collection#add})
         * @throws ClassCastException if the specified element cannot be
         *         compared with elements currently in this priority queue
         *         according to the priority queue's ordering
         * @throws NullPointerException if the specified element is null
         */
    public boolean add(E e) {
        return offer(e);
    }
        
    
     /**
         * Inserts the specified element into this priority queue.
         *
         * @return {@code true} (as specified by {@link Queue#offer})
         * @throws ClassCastException if the specified element cannot be
         *         compared with elements currently in this priority queue
         *         according to the priority queue's ordering
         * @throws NullPointerException if the specified element is null
         */
    public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length) //如果queue数组中的数据满了,扩容
            grow(i + 1);
        siftUp(i, e);   //siftUp,上浮元素
        size = i + 1;
        return true;
    }
    
     private void siftUp(int k, E x) {
            if (comparator != null)//比较器优先
                siftUpUsingComparator(k, x, queue, comparator);
            else
                siftUpComparable(k, x, queue);
        }
    
     /**
         * Increases the capacity of the array.
         *
         * @param minCapacity the desired minimum capacity
         */
        private void grow(int minCapacity) {
            int oldCapacity = queue.length;
            // Double size if small; else grow by 50%
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity < 64 ? oldCapacity + 2 : oldCapacity >> 1
                                               /* preferred growth */);
            queue = Arrays.copyOf(queue, newCapacity);
        }
        
        
        
    
    

    offer方法中我们可以看到的流程

    • 如果queue数组中的数据满了,扩容
    • siftUp,上浮元素
      • 因为元素的父子元素具有单调性,所以可以通过比较,和父节点交换位置,直到找到它合适的位置
        • 这里我们看到的是 比较器(siftUpUsingComparator)优先。
      • 合适的位置指的是 满足 单调性 的位置

    出堆

    /**
         * Retrieves and removes the head of this queue,
         * or returns {@code null} if this queue is empty.
         *
         * @return the head of this queue, or {@code null} if this queue is empty
         */
     public E poll() {
            final Object[] es;
            final E result;
    
            if ((result = (E) ((es = queue)[0])) != null) {//取出第一个元素(索引为0)
                modCount++;
                final int n;
                final E x = (E) es[(n = --size)];//获得最后一个元素
                es[n] = null;//最后一个元素置空
                if (n > 0) {
                    final Comparator<? super E> cmp;
                    if ((cmp = comparator) == null) //比较器优先
                        siftDownComparable(0, x, es, n);//然后做下层操作
                    else
                        siftDownUsingComparator(0, x, es, n, cmp);//然后做下层操作
                }
            }
            return result;
        }
        
        private static <T> void siftDownComparable(int k, T x, Object[] es, int n) {
            // assert n > 0;
            Comparable<? super T> key = (Comparable<? super T>)x;
            int half = n >>> 1;           // loop while a non-leaf
            while (k < half) {
                int child = (k << 1) + 1; // assume left child is least
                Object c = es[child];
                int right = child + 1;
                if (right < n &&
                    ((Comparable<? super T>) c).compareTo((T) es[right]) > 0)
                    c = es[child = right];
                if (key.compareTo((T) c) <= 0)
                    break;
                es[k] = c;
                k = child;
            }
            es[k] = key;
        }
        
          private static <T> void siftDownComparable(int k, T x, Object[] es, int n) {
            // assert n > 0;
            Comparable<? super T> key = (Comparable<? super T>)x;
            int half = n >>> 1;           // loop while a non-leaf
            while (k < half) {
                int child = (k << 1) + 1; // assume left child is least 翻译  假设左孩子是更小的
                Object c = es[child];
                int right = child + 1;
                if (right < n &&
                    ((Comparable<? super T>) c).compareTo((T) es[right]) > 0)
                    c = es[child = right];  //得到较小的孩子
                if (key.compareTo((T) c) <= 0)  //用教小的孩子做比较,做交换
                    break;
                es[k] = c;
                k = child;
            }
            es[k] = key;
        }
    

    poll过程如下

    • 首先取出索引为0的元素,堆顶元素
    • 把索引最大的元素拿到堆顶做下沉操作
      • 如果PriorityQueue构造的时候拥有比较器就用比较器来做下沉比较,否者使用元素继承的Comparable比较性做比较

    Queue常见对外方法

    • boolean add(e) 添加
    • boolean offer(E e) 同add相同
    • E poll() 取出堆顶元素,也就是数组索引为0的元素,size会减少。size=0时会得到 null
    • E peek() 读取堆顶元素,size不会减少。size=0时会得到 null
    • E remove() 和poll()功能相同,只是会@throws NoSuchElementException if this queue is empty
    • E element() 和peek()功能相同,只是会@throws NoSuchElementException if this queue is empty
  • 相关阅读:
    DP -- 递推
    二分查找题
    动态规划
    二分专题
    并查集
    三分法
    二分法
    插入排序
    排序小结
    Go go.mod入门
  • 原文地址:https://www.cnblogs.com/mxjhaima/p/13963640.html
Copyright © 2020-2023  润新知