索引優先隊列的工做原理與簡易實現

歡迎探討,若有錯誤敬請指正 html

如需轉載,請註明出處 http://www.cnblogs.com/nullzx/java


1. 優先隊列與索引優先隊列

優先隊列的原理你們應該比較熟悉,本質上就是利用徹底二叉樹的結構實現以log2n的時間複雜度刪除隊列中的最小對象(這裏以小堆頂爲例)。徹底二叉樹又能夠經過數組下標實現索引,當插入一個對象的時候,利用上浮操做更新最小對象。當刪除堆頂最小對象時,將末尾的對象放置到堆頂上,而後執行下沉操做。算法

優先隊列有一個缺點,就是不能直接訪問已存在於優先隊列中的對象,並更新它們。這個問題在Dijistra算法中就有明顯的體現,有時候咱們須要更新已在隊列中的頂點的距離。爲此就須要設計一種新型的數據結構來解決這個問題,這就是本文要介紹的索引優先隊列。數組

索引優先隊用一個整數和對象進行關聯,當咱們須要跟新該對象的值時,能夠通這個整數進行快速索引,而後對對象的值進行更新。固然更新後的對象在優先隊列中的位置可能發生變化,這樣以保證整個隊列仍是一個優先隊列。數據結構

簡易版的索引優先隊列APIide

IndexPriorityQueue<T>函數

IndexPriorityQueue(int capacity, Comparator<T> cmp)this

構造函數,capacity表示隊列容量,cmp表示對象的比較器spa

void enqueue(int k, T t)設計

將整數k和對象t進行關聯,若是已有和k關聯的對象,則將其更新爲t

int dequeue()

出列,即刪除最對象素並返回與它相關的整數。

void change(int k, T t)

將和整數k和關聯的對象更新爲t

注意與對象關聯的整數k不能超過隊列的容量。

 

2. 索引優先隊列的實現原理

爲了實現快速索引,咱們首先嚐試一個簡單版本。咱們建立兩個數組分別是pq,elements。elements的做用是存儲對象的引用,咱們將每一個對象存儲在與之相關的整數做爲下標的位置中,elements存儲的對象不必定在數組中連續存放。pq存儲是與對象相關的整數值,注意數組pq是連續存放的。此時pq做爲優先隊列,可是在上浮和下沉操做中,咱們比較的是pq中值做爲下標的elements數組中的值。這樣咱們就能夠實現快速索引。

下圖中,咱們以字符串做爲存儲的對象類型,創建一個索引優先隊列

1

從中咱們能夠看出,咱們設計數組pq數組的目的。咱們只須要對pq中的數值進行維護就能夠實現一個優先隊列,而elements中的對象的位置保持不變(出列時會置爲null),這樣就能夠方便咱們快速索引。好比經過elements數組咱們能夠知道與整數3相關的字符串爲「f」。

在圖中,咱們插入一個與整數10相關的字符串「b」後,pq和elements中的值以下圖所示。2

假設在上圖的基礎上,咱們要將與整數3相關的字符串修改成「a」,那麼咱們只須要讓elements[3] = 「a」便可。而後去維護pq中的值。可是在維護pq中的值時出現了一個問題,咱們不知道pq中哪一個位置中的值爲3,只能從都到尾遍歷,找到這個元素所在的位置後進行上浮和下沉操做(由於咱們必須經過下標才能快速找到父節點或者孩子節點)。爲了可以快速找到pq中元素值對應的下標,咱們須要額外設置一個數組qp,它的做用是存儲與對象相關的整數在pq數組中的下標,並在上浮和下沉的過程當中同時維護它。3

在上述的基礎上,假設咱們須要將與整數3相關的字符串修改成「a」,那麼咱們只須要讓elements[3] = 「a」,而後經過qp[3]中的值2就能夠知道數組pq中值爲3的下標爲2,而後對pq[2]進行上浮或下沉操做。這裏顯然須要進行上浮操做,那麼咱們要交換pq[1]和pq[2]的值。這個時候咱們須要注意的是,在交換pq數組中的兩個元素的值時,咱們也須要交換qp對應兩個元素的值,由於與對象相關的整數在pq的不一樣位置上,那麼顯然該整數在pq所在的下標也變了,因此qp中的值也應該發生變化。而須要交換的qp中的兩元素的下標正好就是pq中兩元素的值。結果以下圖所示。因此咱們也須要交換qp[3]和qp[10]的值。

4

 

3. 索引優先隊列的代碼實現

上述的索引優先隊列的原理中不能將數字0與對象進行關聯,由於三個數組沒有使用下標爲0的位置。若是要實現與數字0進行關聯,入列時只須要每一個關聯的數字加1;當出列時,咱們只須要將返回的數字減1。

package datastruct;

import java.util.Arrays;
import java.util.Comparator;

public class IndexPriorityQueue<T> {
	private int[] pq;
	private int[] qp;
	private Object[] element;
	private final int capacity;
	private int size;
	private Comparator<? super T> cmp;
	
	
	private static class Cmp<T> implements Comparator<T>{
		@SuppressWarnings({ "unchecked", "rawtypes" })
		@Override
		public int compare(T t1, T t2) {
			return ((Comparable)(t1)).compareTo(t2);
		}
	}
	
	private static void swap(int[] a, int i, int j){
		int tmp;
		tmp = a[i];
		a[i] = a[j];
		a[j] = tmp;
	}
	
	//與對象關聯的整數範圍是[0,capacity-1]
	public IndexPriorityQueue(int capacity, Comparator<T> cmp){
		this.capacity = capacity;
		pq = new int[capacity+1];
		qp = new int[capacity+1];
		Arrays.fill(qp, -1);
		element = new Object[capacity+1];
		if(cmp == null){
			this.cmp = new Cmp<T>();
		}
	}
	
	public void enqueue(int k, T t){
		k++;//使得關聯的整數能夠爲0
		
		if(k > capacity){
			throw new IllegalArgumentException();
		}
		
		if(qp[k] != -1){
			element[k] = t;
			swim(qp[k]);
			sink(qp[k]);
			return;
		}
		
		size++;
		pq[size] = k;
		qp[k] = size;
		element[k] = t;
		
		swim(size);
	}
	
	@SuppressWarnings("unchecked")
	private void swim(int child){
		int parent = child/2;
		while(parent > 0){			
			if(cmp.compare((T)element[pq[child]], (T)element[pq[parent]]) < 0){
				swap(pq, child, parent);
				swap(qp, pq[child], pq[parent]);
				child = parent;
				parent = child/2;
			}else{
				break;
			}
		}
	}
	
	public int dequeue(){
		if(size == 0){
			throw new IllegalArgumentException();
		}
		int r = pq[1];
		element[r] = null;
		swap(pq, size, 1);
		swap(qp, pq[size], pq[1]);
		pq[size] = -1;
		size--;
		sink(1);
		r--;//使得關聯的整數能夠爲0
		return r;
	}
	
	@SuppressWarnings("unchecked")
	private void sink(int parent){
		int child = parent*2;
		while(child <= size){
			if(child + 1 <= size){
				int r = cmp.compare((T)element[pq[child]], (T)element[pq[child+1]]);
				child = r > 0 ? child+1 : child;
			}
			
			if(cmp.compare((T)element[pq[child]], (T)element[pq[parent]]) < 0){
				swap(pq, parent, child);
				swap(qp, pq[parent], pq[child]);
				parent = child;
				child = parent*2;
			}else{
				break;
			}
		}
	}
	
	public void change(int k, T t){
		k++;
		if(qp[k] == -1){
			throw new IllegalArgumentException();
		}
		element[k] = t;
		swim(qp[k]);
		sink(qp[k]);
	}
	
	public int size(){
		return size;
	}
	
	public boolean isEmpty(){
		return size == 0;
	}
	
	public static void main(String[] args){
		IndexPriorityQueue<String> ipq = new IndexPriorityQueue<String>(11, null);
		ipq.enqueue(0, "k");
		ipq.enqueue(6, "d");
		ipq.enqueue(3, "f");
		ipq.enqueue(4, "c");
		ipq.enqueue(0, "a");
		
		while(!ipq.isEmpty()){
			System.out.println(ipq.dequeue());
		}
	}
}

4. 參考內容

[1]. 算法(第4版)Robert Sedgewick 人民郵電出版社

[2]. 索引優先隊列-IndexedPrirotyQueue的原理及實現(源碼)

相關文章
相關標籤/搜索