美文网首页
从源码角度看PriorityQueue优先队列(二叉堆)

从源码角度看PriorityQueue优先队列(二叉堆)

作者: Gorden_Tam | 来源:发表于2018-09-29 17:41 被阅读0次

    PriorityQueue优先队列

    1.堆ADT:

    堆是一颗被完全填满的二叉树(完全二叉树),一棵高度为h的完全二叉树有2h到2(h+1)-1个节点,意味着完全二叉树的高度为O(log n)。

    堆中每个节点X,X的父节点均小于(等于)X,这意味着根节点为队中最小的节点。

    因为二叉堆的规律,可以使用数组表示二叉堆。

    1)若根节点存储在数组的位置为1:

    image.png

    若节点的位置为i,则其两个儿子节点的位置为2i和2i+1.父亲节点位置为i/2向下取整。

    2)若根节点存储在数组的位置为0:

    若节点的位置为i,儿子节点位置为2i+1,2i+2,其父节点的位置为(i-1)>>>1。Priority使用的是这种存储方式。

    2.PriorityQueue(jdk8)

    1)基本属性:继承AbstractQueue,实现Serializable接口

    2)成员变量:

    Private static final DEFAULT_INITIAL_CAPACITY=11;

    transient Object[] queue

    Private int size

    private final Comparator<? Super E> comparator

    transient int modCount;

    3)主要方法:

    扩容机制:

    和array List一样是1.5倍扩容:

    当数组容量小于64时,每次扩容容量增加2.当容量大于64时1.5倍扩容。

        private void grow(int minCapacity) {
            int oldCapacity = queue.length;
            // Double size if small; else grow by 50%
            int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                             (oldCapacity + 2) :
                                             (oldCapacity >> 1));
            // overflow-conscious code
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            queue = Arrays.copyOf(queue, newCapacity);
        }
    

    优先队列的核心为上滤和下滤函数,
    上滤:新增时,元素会添加在完全二叉树的下一个位置,并上滤。

        siftUpUsingComparable(int k,E x){
            while(k>0){
                int parent=(k-1)>>>1;
                Object o=queue[parent];
                if(comparator.compare(x , E(o))>0){
                    break;
                }
                //从添加元素的节点的父亲开始比较,不断向上比较父亲节点与新增节点x的大小,直至某个节点小于等于x.
                Queue[k]=o;
                K=parent;
            }
            Queue[k]=x;
        }
    

    下滤:出队时,会删除最小元素(根节点),此时会产生空穴,下滤,并将最后一个节点x填入合适的空穴之中。此时,从被删除的元素开始向下比较,比较被删除元素左右儿子,选取较小的那个元素t,若t<x,则将t置于被删除的空穴之中,循环直到最后。

    PriorityQueue下滤分为两种,若comparator对象为空则使用siftDownComparable

    否则使用siftDownUsingComparator.

        siftDownUsingComparator(int k,E x){
            int half=size>>>1;
            while(k<half){
                int child=k<<1+1;
                Object c=queue[child];
                int right=child+1;
                if(right<size&&comparator.compare(E(c),queue[right])) //可能存在节点没有右儿子的情况,故增加right<size的判断。
                     c=queue[child=right];
                if(comparator.compare(E(c) , x)>0)
                    break;
                k=child;
                Queue[k]=c;
            }
        }
    

    poll和offer时会调用上滤和下滤函数

        public E poll() {
            if (size == 0)
                return null;
            int s = --size;
            modCount++;
            E result = (E) queue[0];
            E x = (E) queue[s];
            queue[s] = null;
            if (s != 0)
                siftDown(0, x);
            return result;
        }
    
    public boolean offer(E e) {
            if (e == null)
                throw new NullPointerException();
            modCount++;
            int i = size;
            if (i >= queue.length)
                grow(i + 1);
            size = i + 1;
            if (i == 0)
                queue[0] = e;
            else
                siftUp(i, e);
            return true;
        }
    

    相关文章

      网友评论

          本文标题:从源码角度看PriorityQueue优先队列(二叉堆)

          本文链接:https://www.haomeiwen.com/subject/jprboftx.html