MAX-HEAPIFY(A,i) l=LEFT(i) r=RIGHT(i) if l <= A.heap-size and A[l]>A[i] largest=l; if r<= A.heap-size and A[r]>A[i] largest=r; if largest != i exchange A[i] with A[largest] MAX-HEAPIFY(A,largest)
上面的伪代码是一个子过程,该过程的作用就是维护最大堆。
采用的思路是“逐级下沉”的方式,从下标 i 开始调整,如果 i 以及其左右孩子节点都满足最大堆,则不进行下一步,否则调换使得 i 处的节点最大,然后递归调整其子节点。
MAX-HEAPIFY 最大时间为 O(lgN)。HEAP-SORT 子过程需要调用 n 次 MAX-HEAPIFY 子过程,所以时间复杂度为 O(NlogN).
优先级队列
有了以上基础,我们可以接下来看看JDK中优先级队列的实现。
官方对优先级队列给出的介绍大概如下:
基于优先级堆的无界队列
队列元素的是根据它们的自然顺序或者在构造队列时传入的 Comparator 比较器进行排序的。
不允许 null 元素。
最小堆
现在我们来看看,这个类的一些关键代码。
优先级队列的底层是使用一个对象数组来实现的。
1
transient Object[] queue;
插入元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
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; }
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; }
从上面代码中我们可以看到,首先维护最小堆的方式不是递归方式。而是采用了更改新元素 k 的位置,自底向上,逐级比较,如果满足最小堆性质,则直接插入到该位置,结束,如果不满足,则继续往上走,直到满足条件为止。
删除元素
1 2 3 4 5 6 7 8 9 10 11 12
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 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; }
这回是采用“逐级下沉”的方式进行调整。
在位置 k 处插入元素 x,比较 k 处节点与其左右孩子的大小,如果满足最小堆的性质,则直接插入即可,否则更新 k 的值,继续进行比较。
建堆
PriorityQueue 可以根据三种集合建堆:
排序集合
优先级队列
普通集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
public PriorityQueue(Collection<? extends E> c) { if (c instanceof SortedSet<?>) { SortedSet<? extends E> ss = (SortedSet<? extends E>) c; this.comparator = (Comparator<? super E>) ss.comparator(); initElementsFromCollection(ss); } else if (c instanceof PriorityQueue<?>) { PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c; this.comparator = (Comparator<? super E>) pq.comparator(); initFromPriorityQueue(pq); } else { this.comparator = null; initFromCollection(c); } }
针对情况1、2:已经是有序集合,所以直接拷贝到queue中;
针对情况3:普通的无序集合。
1 2 3 4 5 6 7 8
private void initFromCollection(Collection<? extends E> c) { initElementsFromCollection(c); heapify(); } private void heapify() { for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); }