按照下图的配方,走了一遍源码。
凑齐PriorityQueue就可以召唤神龙了。
Ler’s go go go!
结构
1 2 3 4 5 6 7 8 9
| * Priority queue represented as a balanced binary heap: the two * children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The * priority queue is ordered by comparator, or by the elements' * natural ordering, if comparator is null: For each node n in the * heap and each descendant d of n, n <= d. The element with the * lowest value is in queue[0], assuming the queue is nonempty. */ transient Object[] queue;
|
没错这是个数组,为了更好的理解注释的含义,请看下面↓。
满二叉树:
所有的节点都有2个叶子节点,除了最后层叶子节点;
节点数n和深度d的关系 n=2^d-1;
第i层上的节点数为2^(i-1);
第n个节点的父节点:n/2,左子节点:2n,右子节点:2n+1;(参考下图)
完全二叉树:
有且仅有最底层叶子节点不完整就是完全二叉树。(例如:把15去掉)
最小堆:
父节点小于左右子节点的完全二叉树。
转数组:
用数组来存储二叉树后(参见下图)可得,根节点A[0];左子节点a[2n+1];右子节点a[2(n+1)],父节点a[(n-1)/2]。(n为数组下标,从0开始)
是的,优先队列的存储结构大概就是这样推演而来。
heapify() –> siftDown() –> siftDownComparable() –> siftDownUsingComparator()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| * Establishes the heap invariant (described above) in the entire tree, * assuming nothing about the order of the elements prior to the call. */ @SuppressWarnings("unchecked") private void heapify() { for (int i = (size >>> 1) - 1; i >= 0; i--) siftDown(i, (E) queue[i]); } * Inserts item x at position k, maintaining heap invariant by * demoting x down the tree repeatedly until it is less than or * equal to its children or is a leaf. * * @param k the position to fill * @param x the item to insert */ private void siftDown(int k, E x) { if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } @SuppressWarnings("unchecked") private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super 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 && ((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; } @SuppressWarnings("unchecked") 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; }
|
这是任意数组最小堆化的过程。如果是一个合格的最小堆,那么所有的父节点都在数组前半部分,而通过父节点又能得到左右子节点。因此源码一上来就“size >>> 1”(相当于除以2),只需对前半部分进行循环处理,使得循环结束后所有父节点均大于左/右子节点。这里非根父节点会被多次比较到。heapify()后将得到上文所说的最小堆数组。
1 2 3 4 5 6 7 8 9 10 11 12 13
| @SuppressWarnings("unchecked") 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; }
|
poll()的核心也是siftDown,而这里的“siftDown(0, x);”与之前的“siftDown(i, (E) queue[i]);”不同的是,下标0所对应的元素本非x。也就是说,这里进行了个转换:把最后queue[s]替换了queue[0]进行新的最小堆数组化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| * Removes a single instance of the specified element from this queue, * if it is present. More formally, removes an element {@code e} such * that {@code o.equals(e)}, if this queue contains one or more such * elements. Returns {@code true} if and only if this queue contained * the specified element (or equivalently, if this queue changed as a * result of the call). * * @param o element to be removed from this queue, if present * @return {@code true} if this queue changed as a result of the call */ public boolean remove(Object o) { int i = indexOf(o); if (i == -1) return false; else { removeAt(i); return true; } } * Removes the ith element from queue. * * Normally this method leaves the elements at up to i-1, * inclusive, untouched. Under these circumstances, it returns * null. Occasionally, in order to maintain the heap invariant, * it must swap a later element of the list with one earlier than * i. Under these circumstances, this method returns the element * that was previously at the end of the list and is now at some * position before i. This fact is used by iterator.remove so as to * avoid missing traversing elements. */ @SuppressWarnings("unchecked") private E removeAt(int i) { modCount++; int s = --size; if (s == i) queue[i] = null; else { E moved = (E) queue[s]; queue[s] = null; siftDown(i, moved); if (queue[i] == moved) { siftUp(i, moved); if (queue[i] != moved) return moved; } } return null; }
|
如你所见,remove()也用到了siftDown()(同时还有siftUp(),下面介绍)。这里 经过siftDown后,如果queue[i] == moved则表示queue[i]的左右子节点都大于moved,即保证了i节点子树是最小堆,但queue[i]的父节点是否小于moved却未知,故又进行了siftUp。(图片来自【2】)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| * Inserts item x at position k, maintaining heap invariant by * promoting x up the tree until it is greater than or equal to * its parent, or is the root. * * To simplify and speed up coercions and comparisons. the * Comparable and Comparator versions are separated into different * methods that are otherwise identical. (Similarly for siftDown.) * * @param k the position to fill * @param x the item to insert */ private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } @SuppressWarnings("unchecked") 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; } @SuppressWarnings("unchecked") 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; }
|
与之相对的,还有名为siftUpComparable()/siftUpUsingComparator()的方法。在新增元素时被调用。新增元素放在下标为size的位置。这里的down与up指的是被比较对象x的去向。比较后x被赋值给子节点就是down,被赋值给父节点就是up。当然你来写的时候也可能新增时,从上到下循环遍历。
说点什么
PriorityQueue有序;不允许为null;非线程安全;(PriorityBlockingQueue线程安全);没有介绍的地方大抵与其他集合框架相似,如扩容机制等。
优先队列每次出队的元素都是优先级最高(权值最小)的元素,通过比较(Comparator或元素本身自然排序)决定优先级。
记得常来复习啊~~~
更多有意思的内容,欢迎访问笔者小站: rebey.cn
推荐文章:
【1】【深入理解Java集合框架】深入理解Java PriorityQueue;
【2】java集合——Queue;