PriorityQueue的BinaryHeap實現

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





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章