PriorityQueue(優先隊列)有很多應用場景,例如去聽一場音樂會,假如票已經都賣完了,但是還有許多沒票的人在排隊等是否有人退票,如果有人退票,那麼系統就需要把這張票分類給優先級最高的那個排隊者(這裏的優先級可以是排隊的時間,或者交的錢多少等,這裏的優先級條件對應priorityqueue中的key)。
PriorityQueue的實現方法有多種,包括SortedList,UnSortedList以及BinaryHeap。其核心操作爲insert()與removeMin(),BinaryHeap看似實現比其他兩種複雜,但是在這兩種核心操作的效率上卻優於另外兩個。
下面介紹一下BinaryHeap,BinaryHeap就是一種特殊的complete binary tree(完全二叉樹),它的每個節點的key值要大於它的父親節點。所以最小的節點永遠是根節點。又由於它是一棵完全二叉樹,因此用一維數組來實現。數組的下標能夠映射出節點間的父子關係,其中節點i的父節點的下標爲floor(i / 2), i的左右孩子節點分別爲2i與2i+1。爲了映射出這種關係,在實現一維數組的時候數組的第一個元素爲空,因爲下標0將打破這種映射關係。元素按層次遍歷順序存儲在數組中,如下圖的一個BinaryHeap的存儲:
對於核心操作removeMin(),因爲根節點是最小的節點,因此每次都是remove根節點,根節點remove後我們把二叉樹的最後一個葉子節點(上圖的節點8)放在根節點的位置,放完後爲了保持前面提到的二叉樹的節點順序屬性,我們將該跟節點與它的左右孩子節點比較,如果它大於其中任何一個或者兩個,將它與最小的那個做位置交換,然後繼續該比較,直到該節點找到它的合適位置(小於它的子節點或者已經是葉子節點),這樣操作後二叉樹又保持了原有的屬性。如下圖:
對於核心操作insert(),現將插入的節點插入到樹的最後一個葉子節點後發,也就是數組的最後一個元素後面(這裏涉及到數組已滿重新分配數組的問題),然後將該節點與它的父親節點比較,如果它小於父親節點,則將它與父親節點對換位置,執行此操作直到它大於父親節點或者已經到達根節點。如下圖:
另外一個重要操作是給出一些數後怎麼構造一個BinaryHeap,一個直接方法是按照上述方法一個一個把節點插入進去,但是這種方法的複雜度爲O(nlgn),可以用下邊這個複雜度爲Theta(n)。這種方法是先將這些數扔進實現BinaryHeap的數組,也就是組成一顆不是BinaryHeap的完全二叉樹,然後找到這棵樹的最後一個非葉子節點,然後對這個節點採用與remove()中類似的方法找到該節點的合適位置,完成後繼續對該節點原來位置的前一個節點進行該操作。如下圖:
這種BirnaryHeap在insert()與removeMin()操作與其他兩種實現方法(SortedList與UnSortedList)的比較如下圖:
具體實現代碼:
PriorityQueue接口:
public interface PriorityQueue {
public int size();
public boolean isEmpty();
Entry insert(int key, Object value);
Entry min();
Entry removeMin();
}
Entry類:
public class Entry {
int key;
Object value;
public Entry(int key, Object value){
this.key = key;
this.value = value;
}
}
BinaryHeap類:
public class BinaryHeap implements PriorityQueue{
Entry[] entries;
public BinaryHeap(int i){
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
entries = new Entry[2 * (i + 1)];
for(int j = 0; j < i; j++){
int key = rand.nextInt(1000);
Object value = "value is:" + key;
entries[j + 1] = new Entry(key,value);
}
int bubbleIndx = (int) Math.floor(i / 2);
while(bubbleIndx > 0){
downSwap(bubbleIndx);
bubbleIndx--;
}
}
private void downSwap(int bubbleIndx) {
while(bubbleIndx * 2 <= this.size() || bubbleIndx * 2 + 1 <= this.size()){
int leftChildIndx = bubbleIndx * 2;
int rightChildIndx = bubbleIndx * 2 + 1;
Entry temp = entries[bubbleIndx];
if(entries[rightChildIndx] != null){
if(entries[bubbleIndx].key > entries[leftChildIndx].key ||
entries[bubbleIndx].key > entries[rightChildIndx].key){
if(entries[leftChildIndx].key <= entries[rightChildIndx].key){
entries[bubbleIndx] = entries[leftChildIndx];
entries[leftChildIndx] = temp;
bubbleIndx = leftChildIndx;
}else{
entries[bubbleIndx] = entries[rightChildIndx];
entries[rightChildIndx] = temp;
bubbleIndx = rightChildIndx;
}
}else{
break;
}
}else{
if(entries[bubbleIndx].key > entries[leftChildIndx].key){
entries[bubbleIndx] = entries[leftChildIndx];
entries[leftChildIndx] = temp;
}
// bubbleIndx = leftChildIndx;
break;
}
}
}
@Override
public int size() {
int size = 0;
int index = 1;
while(index < entries.length && entries[index] != null){
size++;
index++;
}
return size;
}
@Override
public boolean isEmpty() {
return entries[1] == null;
}
@Override
public Entry insert(int key, Object value) {
Entry insertEntry = new Entry(key, value);
if(this.size() == this.entries.length - 1){
Entry[] oldEntries = this.entries;
this.entries = new Entry[entries.length * 2];
for(int i = 1; i < oldEntries.length; i++){
entries[i] = oldEntries[i];
}
}
entries[this.size() + 1] = insertEntry;
int bubbleIndx = this.size();
swapUp(bubbleIndx);
return insertEntry;
}
private void swapUp(int bubbleIndx) {
while(bubbleIndx > 1 && entries[bubbleIndx].key < entries[(int) Math.floor(bubbleIndx / 2)].key){
int parentIndx = (int) Math.floor(bubbleIndx / 2);
Entry temp = entries[bubbleIndx];
entries[bubbleIndx] = entries[parentIndx];
entries[parentIndx] = temp;
bubbleIndx = parentIndx;
}
}
@Override
public Entry min() {
return entries[1];
}
@Override
public Entry removeMin() {
if(this.isEmpty()){
return null;
}
Entry minEntry = this.min();
entries[1] = entries[this.size()];
entries[this.size()] = null;
downSwap(1);
return minEntry;
}
}
測試類:
public class Test {
public static void main(String[] args){
BinaryHeap bHeap = new BinaryHeap(10);
for(int i = 1; i <= bHeap.size(); i++){
System.out.print(bHeap.entries[i].key + ",");
}
System.out.println("The size is:" + bHeap.size());
bHeap.removeMin();
for(int i = 1; i <= bHeap.size(); i++){
System.out.print(bHeap.entries[i].key + ",");
}
System.out.println("The size is:" + bHeap.size());
bHeap.insert(2900, "this is the largest");
bHeap.insert(1, "this is the smallest");
bHeap.insert(1, "this is the smallest");
bHeap.insert(1, "this is the smallest");
for(int i = 1; i <= bHeap.size(); i++){
System.out.print(bHeap.entries[i].key + ",");
}
System.out.println("The size is:" + bHeap.size());
}
}