第八節:隊列簡介、手擼順序隊列、手擼鏈隊列和隊列的應用


一. 隊列簡介

 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}");
            }
View Code

運行結果:

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;
        }
    }
View Code

測試

     {
                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++;
        }
    }
View Code

測試

 {
                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 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM