隊列——也是一種操作受限的線性表數據結構

隊列

  • 先進先出,後進後出
  • 和棧的入棧push、出棧pop類似,隊列提供入隊enqueue、出隊dequeue兩種操作,也是一種操作受限的線性表數據結構
  • 常用:循環隊列、阻塞隊列、併發隊列
  • 基於數組:順序隊列
  • 基於鏈表:鏈式隊列

順序隊列

//java實現一個順序隊列
public class ArrayQueue{
	private String[] items;
	//數組大小
	private int n = 0;
	
	private int head = 0;
	private int tail = 0;
	//構造
	public ArrayQueue(int capacity){
		this.items = new String[capacity];
		this.n = capacity;
	}
	
	//入隊enqueue
	public boolean enqueue(String item){
		if(tail==n){
			//隊列滿
			return false;
		}else{
			items[tail] = item;
			++tail;
			return true;
		}
		
	}
	//出隊dequeue
	public String dequeue(){
		if(head==tail){
			//隊列空
			return null;
		}else{
			String res = items[head];
			++head;
			return res;
		}
	}
	
}

  • 和棧不同的是,棧只有一個棧頂指針,出入都在棧頂。
  • 而隊列需要頭指針head和尾指針tail,head用於出隊,tail用於入隊。
    問題:
    tail已經移到隊列最尾部,head經過幾次出隊在數組中間,此時隊列的數組前面還是空的,但卻無法做入隊操作?
    **答:**參考數組刪除元素,數據搬移問題的解決辦法,通過標記出隊的元素,當隊尾沒有空間但隊頭有空間,一次性清楚被標記的元素,並做數據搬移。
    修改入隊enqueue()方法:
public boolean enqueue(String item){
	if(tail==n){
		//隊尾沒有空間
		if(head==0){
			//隊頭沒有空間
			return false;
		}else{
			//隊頭有空間
			//數據搬移
			for(int i=head;i<tail;++i){
				items[i-head] = items[i];
			}
			tail = tail-head;
			head = 0;
			
		}
		
	}
}

出隊: O(1)
入隊: 最好O(1),最壞O(n),均攤O(1)

循環隊列

  • 當隊尾沒有空間時,隊頭還有空間,tail指向下標0。
  • 例如:隊列容量n爲9,某時tail指向8,當有元素入隊將元素放在下標8的位置,tail此時不指向9,而時指向0;
  • 假如此時head=0,tail也即將指向0,那麼此時隊列在入隊操作後就滿了;

問題關鍵在於 怎樣確定隊空和隊滿?
非循環隊列:隊空 head=tail ,隊滿 tail=n
循環隊列: 隊空 head=tail, 隊滿 (tail+1)%n=head

//順序循環隊列
public class CircularQueue {
  // 數組:items,數組大小:n
  private String[] items;
  private int n = 0;
  // head表示隊頭下標,tail表示隊尾下標
  private int head = 0;
  private int tail = 0;

  // 申請一個大小爲capacity的數組
  public CircularQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入隊
  public boolean enqueue(String item) {
    // 隊列滿了
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // 出隊
  public String dequeue() {
    // 如果head == tail 表示隊列爲空
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
  }
}

阻塞隊列

  • 阻塞隊列其實就是在隊列基礎上增加了阻塞操作。
  • 在隊列爲空的時候,從隊頭取數據會被阻塞,直到隊列中有了數據才能返回;
  • 如果隊列已經滿了,則插入數據的操作就會被阻塞,直到隊列中有空閒位置後再插入數據。

併發隊列

  • 線程安全的隊列我們叫作併發隊列。
  • 最簡單直接的實現方式是直接在 enqueue()、dequeue() 方法上加鎖,但是鎖粒度大併發度會比較低,同一時刻僅允許一個存或者取操作。
  • 實際上,基於數組的循環隊列,利用 CAS 原子操作,可以實現非常高效的併發隊列。
  • 這也是循環隊列比鏈式隊列應用更加廣泛的原因。

熄燈

隊列應用場景:

  • 對於大部分資源有限的場景,當沒有空閒資源時,基本上都可以通過“隊列”這種數據結構來實現請求排隊。
  • 例如:線程池,數據庫連接池等。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章