資料結構-佇列(Queue )
一、什麼是佇列?
1. 先進者先出 ,這就是典型的“佇列”結構。
2.支援兩個操作:入隊enqueue(),放一個數據到隊尾;出隊dequeue(),從隊頭取一個元素。
3.所以和棧一樣,佇列也是一種操作受限的線性表。

1.png
二、如何實現佇列?
1.佇列API
public interface Queue<T> {
public void enqueue(T item); //入隊
public T dequeue(); //出隊
public int size(); //統計元素數量
public boolean isNull(); //是否為空
}
public class Queue { private string[] items; private int n = 0; private int head = 0; private int tail = 0; public Queue(int capacity) { items = new string[capacity]; n = capacity; } /// <summary> /// 入隊 /// </summary> /// <param name="str"></param> /// <returns></returns> public bool enqueue(string str) { //表示隊尾沒有空間了 if (tail == n) { //佇列已滿, if (head == 0) return false; //當隊尾沒有空間,對頭有空間時,將資料遷移 for (int i = head; i < tail; i++) { items[i - head] = items[i]; } tail -= head; head = 0; } items[tail] = str; tail++; return true; } /// <summary> /// 出隊 /// </summary> /// <returns></returns> public string dequeue() { //佇列為空 if (tail == head) return ""; string s = items[head]; head++; return s; } }
三、佇列有哪些常見的應用?
1.阻塞佇列
1)在佇列的基礎上增加阻塞操作,就成了阻塞佇列。
2)阻塞佇列就是在佇列為空的時候,從隊頭取資料會被阻塞,因為此時還沒有資料可取,直到佇列中有了資料才能返回;如果佇列已經滿了,那麼插入資料的操作就會被阻塞,直到佇列中有空閒位置後再插入資料,然後在返回。
3)從上面的定義可以看出這就是一個“生產者-消費者模型”。這種基於阻塞佇列實現的“生產者-消費者模型”可以有效地協調生產和消費的速度。當“生產者”生產資料的速度過快,“消費者”來不及消費時,儲存資料的佇列很快就會滿了,這時生產者就阻塞等待,直到“消費者”消費了資料,“生產者”才會被喚醒繼續生產。不僅如此,基於阻塞佇列,我們還可以通過協調“生產者”和“消費者”的個數,來提高資料處理效率,比如配置幾個消費者,來應對一個生產者。
2.併發佇列
1)在多執行緒的情況下,會有多個執行緒同時操作佇列,這時就會存線上程安全問題。能夠有效解決執行緒安全問題的佇列就稱為併發佇列。
2)併發佇列簡單的實現就是在enqueue()、dequeue()方法上加鎖,但是鎖粒度大併發度會比較低,同一時刻僅允許一個存或取操作。
3)實際上,基於陣列的迴圈佇列利用CAS原子操作,可以實現非常高效的併發佇列。這也是迴圈佇列比鏈式佇列應用更加廣泛的原因。
3.執行緒池資源枯竭是的處理
在資源有限的場景,當沒有空閒資源時,基本上都可以通過“佇列”這種資料結構來實現請求排隊。