一. 隊列簡介
1. 什么是隊列
隊列(Queue)只允許在一端進行插入,在另一端進行刪除的線性表。(隊尾入隊,隊頭出隊),可見隊列具有先進先出(First In First Out)或后進后出( Last In Last Out)的特性。
C#中提供Queue隊列類,它不是線程安全的; 如需要使用線程安全的隊列類,則使用:ConcurrentQueue
2. 名詞
隊頭( Front):隊列中允許數據刪除的那一端。
隊尾( Rear):隊列中允許數據插入的那一端。
隊上溢(Full):存儲空間已滿,仍希望入隊操作,會產生上溢出,是一種空間不足的出錯狀態。
隊下溢(Empty):隊內空間無數據,仍希望出隊操作,會產生下溢出,是一種數據不足的出錯狀態。
空隊列(Empty Queue):隊列中沒有數據元素.
入隊(Enqueue):將一個數據插入到隊尾的操作。
出隊(Dequeue):讀取隊頭節點並刪除該節點的操作
3. 常用Api
Enqueue()入隊(放在隊尾)
Dequeue()出隊(移除隊首元素,並返回被移除的元素)
Peek()取得隊首的元素,不移除
Clear()清空元素
Count獲取隊列中元素的個數
測試代碼:

{ Console.WriteLine("--------------C#提供的Queue---------------------"); Queue<int> s1 = new Queue<int>(); s1.Enqueue(1); s1.Enqueue(2); s1.Enqueue(3); s1.Enqueue(4); Console.WriteLine($"元素的個數為:{s1.Count}"); int p1 = s1.Dequeue(); //取出並刪除 Console.WriteLine($"元素的個數為:{s1.Count},取出的數據為:{p1}"); int p2 = s1.Peek(); //取出不刪除 Console.WriteLine($"元素的個數為:{s1.Count},取出的數據為:{p2}"); s1.Clear(); Console.WriteLine($"元素的個數為:{s1.Count}"); }
運行結果:
4. 分類
隊列存儲結構的實現有以下兩種方式:
順序隊列:在順序表的基礎上實現的隊列結構。
鏈隊列:在鏈表的基礎上實現的隊列結構。
兩者的區別僅是順序表和鏈表的區別,即在實際的物理空間中,數據集中存儲的隊列是順序隊列,分散存儲的隊列是鏈隊列。
二. 順序隊列
1. 順序隊列
順序隊列(Sequence Queue)用一片連續的存儲空間來存儲隊列中的數據元素.用一維數組來存放順序隊列中的數據元素。隊頭位置設在數組下標為 0 的端,用 front 表示;隊尾位置設在數組的另一端,用 tail 表示。 front 和 tail 隨着插入和刪除而變化。當隊列為空時, front=tail=0。因為在出隊列(刪除元素)的時候,需要花費大量的時間移動大量元素,速度很慢,所以很少有實際應用.
2. 順序循環隊列
為了避免大量數據的移動,通常將一維數組的各個元素看成一個收尾相接的封閉的圓環,即第一個元素是最后一個元素的下一個元素,這種形式的順序隊列稱為循環順序隊列(Circular sequence Queue)。
注:C#為我們提供的Queue類就是循環隊列。
3. 手擼循環隊列
接口
/// <summary> /// 聲明隊列接口 /// </summary> /// <typeparam name="T"></typeparam> public interface IQueue<T> { int Count { get; }//獲取元素個數 bool IsEmpty(); //是否為空隊列 void Clear(); //清空隊列 void Enqueue(T item); //入隊 T Dequeue(); //出隊 T Peek(); //取隊頭元素 }
代碼

/// <summary> /// 循環隊列 /// </summary> /// <typeparam name="T"></typeparam> public class SeqQueue<T> : IQueue<T> { //存放元素的數組 public T[] _array; //增長因子 1-10之間 private int _growfactor; //最小增長值 private const int _MinimumGrow = 4; //默認隊列空間的大小 private const int _defaultCapacity = 8; //元素的個數 private int _size = 0; //隊頭指針 指向隊頭的第一個元素 private int _head; //隊尾指針 指向隊尾的最后一個元素索引+1 private int _tail; public SeqQueue() : this(_defaultCapacity, 2) { } public SeqQueue(int capacity, float growFactor) { if (capacity < 0) { throw new ArgumentOutOfRangeException("capacity", "初識容量不能小於0"); } if (capacity < _defaultCapacity) { capacity = _defaultCapacity; } if (growFactor < 1.0 || growFactor > 10.0) { throw new ArgumentOutOfRangeException("growFactor", "增長因子必須在1-10之間"); } this._array = new T[capacity]; //初始化數組 this._head = this._tail = 0; this._size = 0; this._growfactor = (int)(growFactor * 100f); } //獲取元素個數 public int Count { get { return this._size; } } public void Clear() { this._head = this._tail = this._size = 0; } //出隊操作 public T Dequeue() { if (this._size == 0) { throw new InvalidOperationException("隊列下溢,隊里里沒有數據"); } T obj = this._array[_head]; //出隊數據 this._array[_head] = default(T); //在數組里刪除出隊元素 this._head = (this._head + 1) % this._array.Length; //循環隊列 處理機制,保證下標是循環的 this._size--; return obj; } //入隊 public void Enqueue(T item) { if (this._size == this._array.Length) { //計算新的容量 int capacity = (int)(this._array.Length * this._growfactor / 100f); if (capacity < this._array.Length + _MinimumGrow) { //最少要增長四個元素 capacity = this._array.Length + _MinimumGrow; } //調整容量 SetCapacity(capacity); } this._array[_tail] = item; //入隊 this._tail = (this._tail + 1) % this._array.Length; //移動尾巴指針 this._size++; } private void SetCapacity(int capacity) { //內存搬家 T[] destinationArray = new T[capacity]; if (this._head < this._tail) { //頭指針在尾指針的前面 Array.Copy(this._array, this._head, destinationArray, 0, this._size); } else { //頭指針在尾指針的后面 Array.Copy(this._array, this._head, destinationArray, 0, this._array.Length - this._head); Array.Copy(this._array, 0, destinationArray, this._array.Length - this._head, this._tail); } this._array = destinationArray; this._head = 0; this._tail = (this._size == capacity) ? 0 : this._size; } public bool IsEmpty() { return this._size == 0; } //獲取隊頭元素,不刪除 public T Peek() { if (this._size == 0) { throw new InvalidOperationException("隊列下溢,隊里里沒有數據"); } T obj = this._array[_head]; //出隊數據 return obj; } }
測試
{ Console.WriteLine("--------------手擼循環隊列---------------------"); IQueue<int> s1 = new SeqQueue<int>(); s1.Enqueue(1); s1.Enqueue(2); s1.Enqueue(3); s1.Enqueue(4); Console.WriteLine($"元素的個數為:{s1.Count}"); int p1 = s1.Dequeue(); //取出並刪除 Console.WriteLine($"元素的個數為:{s1.Count},取出的數據為:{p1}"); int p2 = s1.Peek(); //取出不刪除 Console.WriteLine($"元素的個數為:{s1.Count},取出的數據為:{p2}"); s1.Clear(); Console.WriteLine($"元素的個數為:{s1.Count}"); }
運行結果
三. 鏈隊列
1. 含義
隊列的另外一種存儲方式是鏈式存儲,這樣的隊列稱為鏈隊列(Linked Queue)。同鏈棧一樣,鏈隊列通常用單鏈表來表示,它的實現是單鏈表的簡化。
2. 手擼鏈隊列
接口
/// <summary> /// 聲明隊列接口 /// </summary> /// <typeparam name="T"></typeparam> public interface IQueue<T> { int Count { get; }//獲取元素個數 bool IsEmpty(); //是否為空隊列 void Clear(); //清空隊列 void Enqueue(T item); //入隊 T Dequeue(); //出隊 T Peek(); //取隊頭元素 }
代碼

/// <summary> /// 鏈隊列 /// (隊尾tail入隊,隊頭front出隊) /// </summary> /// <typeparam name="T"></typeparam> public class LinkedQueue<T> : IQueue<T> { public QueueNode<T> front; //隊頭節點 public QueueNode<T> tail; //隊尾節點 public int count; //元素個數 public LinkedQueue() { Clear(); } public int Count { get { return count; } } /// <summary> /// 清空所有元素 /// </summary> public void Clear() { front = null; tail = null; count = 0; } /// <summary> /// 判空 /// </summary> /// <returns></returns> public bool IsEmpty() { return count == 0; } /// <summary> /// 出隊(不刪除元素) /// </summary> /// <returns></returns> public T Peek() { if (front == null) { throw new ArgumentOutOfRangeException("隊列下溢,隊列中沒有元素"); } T data = front.data; return data; } /// <summary> /// 出隊(刪除元素) /// </summary> /// <returns></returns> public T Dequeue() { if (front == null) { throw new ArgumentOutOfRangeException("隊列下溢,隊列中沒有元素"); } T data = front.data; if (count == 1) { front = null; tail = null; } else { front = front.next; } count--; return data; } /// <summary> /// 入隊 /// </summary> /// <param name="item"></param> public void Enqueue(T item) { QueueNode<T> newNode = new QueueNode<T>(item); if (count == 0) { front = newNode; tail = newNode; } else { tail.next = newNode; tail = newNode; } count++; } }
測試
{ Console.WriteLine("--------------手擼鏈隊列---------------------"); IQueue<int> s1 = new LinkedQueue<int>(); s1.Enqueue(1); s1.Enqueue(2); s1.Enqueue(3); s1.Enqueue(4); Console.WriteLine($"元素的個數為:{s1.Count}"); int p1 = s1.Dequeue(); //取出並刪除 Console.WriteLine($"元素的個數為:{s1.Count},取出的數據為:{p1}"); int p2 = s1.Peek(); //取出不刪除 Console.WriteLine($"元素的個數為:{s1.Count},取出的數據為:{p2}"); s1.Clear(); Console.WriteLine($"元素的個數為:{s1.Count}"); }
運行結果
四. 應用
1. 流量削峰
高並發場景,比如秒殺,可以把下單請求存放到隊列中,然后消費者從隊列中依次取出來進行實際下單業務。
2. 應用解耦(異步)
比如登錄成功后,要增加積分或者發送郵件,可以引入消息隊列進行解耦,異步處理增加積分 或者 發送郵件的請求。
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。