轉載: http://blog.csdn.net/lisuyibmd/article/details/53205403
一、什麼是優先隊列
優先隊列不是按照普通對象先進先出原FIFO則進行數據操作,其中的元素有優先級屬性,優先級高的元素先出隊。本文提到的PriorityQueue隊列,是基於最小堆原理實現。
需要注意:PriorityQueue繼承了AbstractQueue沒有實現BlockingQueue接口,所以沒有take阻塞方法。
二、什麼是最小堆
最小堆是一個完全二叉樹,所謂的完全二叉樹是一種沒有空節點的二叉樹。
最小堆的完全二叉樹有一個特性是根節點必定是最小節點,子女節點一定大於其父節點。還有一個特性是葉子節點數量=全部非葉子節點數量+1
在 PriorityQueue隊列中,基於數組保存了完全二叉樹。所以在已知任意一個節點在數組中的位置,就可以通過一個公式推算出其左子樹和右子樹的下標。已知節點下標是i,那麼他的左子樹是2*i+1,右子樹是2*i+2。
三、PriorityQueue隊列的存取原理
1、首先看下源碼
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
/**
* 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);
}
在調用offer(E e)方法入隊時,首先進行非null判斷,然後是grow方法擴容,緊接着就是siftUp方法調整節點位置,那麼是如何調整的呢?爲什麼要調整?
2、爲什麼要調整節點順序?
因爲這是一個最小堆,最小堆必須要嚴格按照子節點大於父親節點的順序做數組中存放。
3、如何調整?
siftup方法有個if-else判斷,如果有比較器,則使用siftUpUsingComparator(k, x);如果沒有則調用siftUpComparable(k, x);這個方法會默認給一個比較器。
比較什麼呢??我們說最小堆實現了這個隊列,隊列一定有入隊操作,入隊是元素首先進入隊列尾,然後和自己的父節點比較,像冒泡一樣將該節點冒到合適的位置,即比自己的父親節點大,比自己的兒子節點小。
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
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;
}
4、如何出隊
[java] view plain copy 在CODE上查看代碼片派生到我的代碼片
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;
}
出隊和入隊原理正好相反,每次出隊也要進行siftDown操作,對元素順序重新整理。
四、總結
PriorityQueue隊列不適合進場出隊入隊的頻繁操作,但是他的優先級特性非常適合一些對順序有要求的數據處理場合。
版權聲明:沉澱、積累,我追求工匠精神