1. 程式人生 > >基於堆的優先佇列(Java實現)

基於堆的優先佇列(Java實現)

優先佇列的最重要的操作:刪除最大元素(或最小)和插入元素。資料結構二叉堆能夠很好的實現佇列的基本操作。
二叉堆的結點按照層級順序放入陣列,用長度為N+1的私有陣列pq來表示一個大小為N的堆(堆元素放在pq[1]至pq[N]之間,為方便計數,未使用pq[0]),跟節點在位置1,它的子結點在位置2和3,以此類推。位置k的節點的父節點位置為k/2,它的兩個子節點位置分別為2k和2k+1。
當一顆二叉樹的每個節點都大於等於它的兩個子節點時,稱為大根堆。
當一顆二叉樹的每個節點都小於等於它的兩個子節點時,稱為小根堆。

堆(大根堆)的有序化:

  • 由下至上的堆有序化(上浮):如果堆的有序狀態因為某個節點變得比它的父節點更大而被打破,就需要交換它和它的父節點來修復堆,以此類推,直到遇到一個更大的父節點。
private void swim(int k){
	while(k > 1 && pq[k] > pq[k / 2]){
		int temp = pq[k];
		pq[k] = pq[k / 2];
		pq[k / 2] = temp;
		k /= 2;
	}
}
  • 由上至下的堆有序化(下沉):如果堆的有序狀態因為某個節點變得比它的兩個子節點或者其中之一更小而被打破,需要將它和它的兩個子節點中的較大者交換來恢復堆,以此類推,直到它的子節點比它更小或者到達堆的底部。
private void sink(int k){
	while(2 * k <= N){
		int j = 2 * k;
		if(j < N && pq[j] < pq[j + 1]){
			j++;
		}
		if(pq[k] >= pq[j]){
			break;
		}
		int temp = pq[k];
		pq[k] = pq[j];
		pq[j] = temp;
		k = j;
	}
}

基於堆的優先佇列的Java程式碼實現:

public class MaxPQ<Key>{
	private Key[] pq;
	private int N = 0;
	public MaxPQ(int maxN){
		pq = new Key[maxN + 1];
	}
	public boolean isEmpty(){
		return N == 0;
	}
	public int size(){
		return N;
	}
	public void insert(Key v){
		pq[++N] = v;//陣列擴容由讀者自行實現
		swim(N);//插入到堆末尾,然後進行上浮
	}
	public Key delMax(){
		Key max = pq[1];//從跟節點得到最大元素
		exchange(1, N--);//將根節點的元素和最後一個節點交換
		pq[N] = null;
		sink(1);//下沉
		return max;
	}
	public void exchange(int i, int j){
		Key temp = pq[i];
		pq[i] = pq[j];
		pq[j] = temp;
	}
}

對於一個含有N個元素的基於堆的優先佇列,刪除最大元素和插入操作時間複雜度為O(logN)。