概述
隊列(queue)是一種只允許在一端進行插入操作,而在另一端進行刪除操作的線性表。
隊列是一種先進先出(First In First Out)的線性表,簡稱FIFO。
允許插入的一端稱為隊尾,允許刪除的一端稱為隊頭。
因為已經限制了插入和刪除的位置,所以對於隊列,插入和刪除時只需要考慮滿和空兩種狀態。
線性表存儲結構分為順序存儲和鏈式存儲,這里只討論靜態分配的順序存儲結構。
約定
為了方便起見,我們約定:
1、初始化建隊列時,令隊頭指針m_nFront和隊尾指針m_nRear等於0,即m_nFront = m_nRear = 0;
2、m_nFront指向隊頭元素的位置,m_nRear指向隊尾元素的下一位。
關鍵點
1、順序隊列的假上溢現象
順序隊列的操作分別在隊頭和隊尾兩端進行。在出隊時,隊頭m_nFront和隊尾m_nRear的值都是只增加(向隊列長度m_nCount)靠近;如果僅通過m_nRear == m_nCount來判斷順序隊列是否滿隊,此時可能存在m_nRear已經指向m_nCount,同時m_nFront > 0(已有元素出隊),順序隊列中實際的元素個數遠小於m_nCount而不能做入隊操作的情況,導致元素出隊后的空閑存儲空間永遠無法重用,造成假上溢。如下圖:
解決方法:
為克服假上溢,可將順序隊列想象為一個首尾相接的環狀空間,稱為循環隊列。在循環隊列中出隊入隊時,頭尾指針仍向前移動進行加1操作,當頭尾指針指向m_nCount時,頭尾指針加1操作的結果重新指向下界0(加1后對m_nCount做取余數運算)。
2、判斷隊空和隊滿
想象成循環隊列后,當入隊時,m_nRear向前追趕m_nFront,出隊時,m_nFront向前追趕m_nRear,故存在隊空和隊滿時都有m_nFront == m_nRear的情況,因此無法通過m_nFront == m_nRear來判斷隊空還是隊滿。
解決方法:
犧牲存儲空間中的一個存儲單元,使隊空和隊滿的判斷條件不同即可,具體的:
1)出隊時,m_nRear == m_nFront時,表示隊空,此時不能出隊。
2)入隊時,當(m_nRear + 1) % m_nCount == m_nFront時,表示隊滿,此時不能入隊。
代碼實現
Queue.h
/******************************************************* * File Name:Queue.h * Description:隊列類頭文件,自主實現一個隊列數據結構 * Version:V1.0 * Author:Mengjia * Date:2018-06-02 * Copyright (C)2018, Mengjia * Others:自主實現一個隊列數據結構 #pragma once *******************************************************/ template<class T> class CQueue { private: T * m_pData; //數據指針 int m_nCount; //隊列長度 int m_nFront; //當前隊列頭部 int m_nRear; //當前隊列尾部 public: //構造函數 CQueue(int nCount); //析構函數 ~CQueue() { delete[] m_pData; } //入列 void Enqueue(const T& data); //出列 bool Dequeue(T& data); //判斷隊滿 bool isFull() { return (m_nRear + 1) % m_nCount == m_nFront; } //判斷隊空 bool isEmpty() { return m_nFront == m_nRear; } //遍歷輸出隊列內容 //注意,如果模板參數傳入的為非基礎數據類型,則無法使用<<進行流的輸出 void TraverseQueue(); //清空隊列內容 void ClearQueue(); //返回Queue的元素個數,即當前隊列長度 int QueueLength() { return (m_nRear - m_nFront + m_nCount) % m_nCount; } }; template<class T> CQueue<T>::CQueue(int nCount) { m_pData = new T[nCount]; m_nCount = nCount; m_nFront = m_nRear = 0; //頭尾初始化為0 } template<class T> void CQueue<T>::Enqueue(const T& data) { if (!isFull()) { m_pData[m_nRear] = data; m_nRear = (m_nRear + 1) % m_nCount; //隊列尾部前移 } } template<class T> bool CQueue<T>::Dequeue(T& data) { if (isEmpty()) return false; data = m_pData[m_nFront]; m_pData[m_nFront] = 0; //出列后該位置數據清零 m_nFront = (m_nFront + 1) % m_nCount; //隊列頭部前移 return true; } template<class T> void CQueue<T>::TraverseQueue() { int i = m_nFront; while (i != m_nRear) { cout << m_pData[i] << endl; i = (i + 1) % m_nCount; } } template<class T> void CQueue<T>::ClearQueue() { int i = m_nFront; while (i != m_nRear) { m_pData[i] = 0; i = (i + 1) % m_nCount; } m_nFront = m_nRear = 0; }
main.cpp
/******************************************************* * File Name:main.cpp * Description:用於演示隊列數據結構 * Version:V1.0 * Author:Mengjia * Date:2018-06-02 * Copyright (C)2018, Mengjia * Others: * Blog: *******************************************************/ #include "Queue.h" #include <iostream> using namespace std; int main() { CQueue<int> queue(6); int i = 0; while (i < 6) { queue.Enqueue(i++); } int data = 0; queue.Dequeue(data); cout << "The first element of queue: " << data << endl; queue.Dequeue(data); cout << "The second element of queue: " << data << endl; cout << "The Length of queue: " << queue.QueueLength() << endl; queue.Enqueue(5); queue.Enqueue(6); cout << "The Length of queue: " << queue.QueueLength() << endl; cout << "Output all elements of queue: " << endl; queue.TraverseQueue(); queue.ClearQueue(); cout << "The Length of queue: " << queue.QueueLength() << endl; system("pause"); return 0; }
輸出結果: