默认情况下PriorityQueue使用自然排序法,最小元素先出列。
PriorityQueue是一种无界的,线程不安全的队列。
是一种通过数组实现的,并拥有优先级的队列。
存储的元素要求必须是可比较的对象, 如果不是就必须明确指定比较器。

一般而言,堆为二叉树。

(offer)上移,找父节点;(poll)下移,找还左孩子节点和右孩子(若存在)。

offer,poll都会自动调整堆的排列。

!!!!!!!!!!!!!!!!!

但是!!!x与queue[k]的比较,不是k++;而是垂直的,也就是k变为父亲或者孩子。

!!!!!!!!!!!!!!!!!

所以堆不是二叉搜索树,即在poll下移时,需要比较,child和right的大小,调整c。

1. 自定义比较:降序

PriorityQueue<Integer> queue=new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
//            降序o2>o1,o2在前
//            自然序为升序
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });

o2.compareTo(o1)==compare(o2,o1) .返回(o2-o1);若o2>o1,返回1;否则返回0或-1。若o2>o1,则o2在前。

自然序为升序。

补充一下:关于int compare(o1,o2);  return o1-o2; 返回-1,0,1;

在siftOfUp(offer函数的上移)中比较是

int parent = (k - 1) >>> 1;
Object e = queue[parent];
if (key.compareTo((E) e) >= 0)
      break;

若插入的key.compare(堆节点值)>=0    意味着(默认自然序中)插入key>堆节点值,则找到key上移的顶的位置k了,因为是小顶堆,找到比key大的的节点不需要继续比较了,乖乖当儿子就好。

而在siftOfDown(poll的下移)中,返回非正值,key<=c;   插入值key小于等于左右孩子中一个c,所以找到插入x下移的位置了,找到比插入值大的节点就行,乖乖当老子。(堆只能保证最上面的最小或最大,其他的并不是顺序的)。

所以在最小堆中,offer就是儿子找爹,在下面;poll就是老子找儿子,在上面。

if (key.compareTo((E) c) <= 0)
    break;

************************************************************************************************************************************

2. 增

PriorityQueue,add() 调用offer(),自动调整堆

offer源代码:

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;
    }

//调整堆:在k的位置添加x,再调整堆(上移)。
//根据是否自定义compare规则,分为两种:自定义,自然序。
private void siftUp(int k, E x) {
        if (comparator != null)
//        自定义
            siftUpUsingComparator(k, x);
        else
//        自然序
            siftUpComparable(k, x);
    }

//    上移调整堆,k加入位置,x加入元素
/*
1. 找k的父节点e,如果(按照自定义比较)父节点 e > x,则(x小,不用上移了)break,并设置堆的k位置为新加入元素x;
2. 若x大,则上移:将k位置设为父节点的值,k=parent,继续循环(k>>>1,找父节点),直到找到x的新父节点大于x,x的子节点小于x的位置。
tips:>>>无符号右移
*/
private void siftUpUsingComparator(int k, E x) {
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }

//自然序
private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

//增加容量
 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);
    }

3. 删

poll,自动调整堆

poll源码:

先取最后的元素提到根结点,然后删除最大值,然后再把新的根节点放到合适的位置

/*
--size,返回堆顶元素(queue[0])。
将最后一个元素(queue[s])置于堆顶,再下移,调整位置。(siftDown(0,x))
*/
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;
    }

private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }
//将x从k处,下移。(k=0,x=原堆最后一个元素)
/*
循环终止条件:k<size的一半
循环内部:找到x的位置
    child=k的左孩子,c-左孩子的值,right-右孩子
    (左右孩子取大值) if(右孩子不是最后一层 且 右孩子比左孩子值大):c和child变为右孩子的位置。
    (判断是否k为x的位置,此节点的值比x小)if(k的孩子节点比x的值小)break;找到k-x插入的位置了。
     (孩子节点上移)
      (k下移)    k=孩子节点的位置
循环结束:k的位置为x插入的地方。
*/
private void 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, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

//默认
private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

对于优先队列的解释:https://www.cnblogs.com/CarpenterLee/p/5488070.html