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

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号