數據結構基礎(7) --循環隊列的設計與實現

隊列

    隊列簡稱隊, 也是一種操作受限的線性表, 只允許在表的一端進行插入, 而在表的另一端進行刪除.其特點爲”先進先出(FIFO)”,故又稱爲先進先出的線性表,簡單隊列如圖所示:

 

循環隊列

    順序隊列有一個先天不足, 那就是空間利用率不高, 會產生”假溢出”現象,即:其實隊列中還有空閒的空間以存儲元素, 但我們在判斷隊列是否還有空間時, 隊列告訴我們隊列已經滿了, 因此這種溢出並不是真正的溢出, 在data數組中依然存在可以放置元素的空位置, 所以說這是一種”假溢出”;

    於是我們就引入了循環隊列的概念, 將順序隊列臆造爲一個環狀的空間, 即把存儲隊列元素的表從邏輯上看成一個環, 稱爲循環隊列,其示意圖如下:


注意:如圖中所示,我們的循環隊列爲了在實現上的便利, 會有一個位置的空閒, m_front(如圖中的front)指針總會指向一個元素值爲空的位置,因此(m_front+1)%capacity才真正的指向隊首元素, 而m_rear(圖中爲rear)才指向一個真實存在的隊尾元素;

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //循環隊列的實現與解析  
  2. template <typename Type>  
  3. class MyQueue  
  4. {  
  5.     template <typename T>  
  6.     friend ostream &operator<<(std::ostream &os, const MyQueue<T> &queue);  
  7. public:  
  8.     MyQueue(int queueSize = 64);  
  9.     ~MyQueue();  
  10.   
  11.     void push(const Type &item);  
  12.     void pop() throw (std::range_error);  
  13.     const Type &front() const throw (std::range_error);  
  14.     const Type &rear() const throw (std::range_error);  
  15.     bool isEmpty() const;  
  16.   
  17. private:  
  18.     Type *m_queue;  
  19.     int m_front;    //隊首指針(其實(m_front+1)%capacity才真正的指向隊首元素)  
  20.     int m_rear;     //隊尾指針  
  21.     int capacity;   //隊列的內存大小, 但實際可用的大小爲capacity-1  
  22. };  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. MyQueue<Type>::MyQueue(int queueSize): capacity(queueSize)  
  3. {  
  4.     if (queueSize < 1)  
  5.         throw std::range_error("queueSize must >= 1");  
  6.   
  7.     m_queue = new Type[capacity];  
  8.     if (m_queue == NULL)  
  9.         throw std::bad_alloc();  
  10.   
  11.     m_front = m_rear = 0;  
  12. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. MyQueue<Type>::~MyQueue()  
  3. {  
  4.     delete []m_queue;  
  5.     m_queue = NULL;  
  6.     m_front = m_rear = 0;  
  7.     capacity = -1;  
  8. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. inline bool MyQueue<Type>::isEmpty() const  
  3. {  
  4.     return m_front == m_rear;  
  5. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. inline void MyQueue<Type>::push(const Type &item)  
  3. {  
  4.     if ((m_rear+1)%capacity == m_front) //隊列已滿  
  5.     {  
  6.         Type *newQueue = new Type[2 * capacity];    //新隊列的長度爲原隊列的2倍  
  7.         if (newQueue == NULL)  
  8.             throw std::bad_alloc();  
  9.   
  10.         int start = (m_front+1)%capacity;   //數據序列的起始地址  
  11.         if (start <= 1) //隊列指針尚未迴繞  
  12.         {  
  13.             //只需拷貝一次:從start所指向的元素直到m_rear所指向的元素  
  14.             //std::copy(m_queue+start, m_queue+start+capacity-1, newQueue);  
  15.             std::copy(m_queue+start, m_queue+m_rear+1, newQueue);  
  16.         }  
  17.         else  
  18.         {  
  19.             //需要拷貝兩次  
  20.             //1:從start所指向的元素直到數組(不是隊列)末尾  
  21.             std::copy(m_queue+start, m_queue+capacity, newQueue);  
  22.             //2:從數組(不是隊列)起始直到隊列末尾  
  23.             std::copy(m_queue, m_queue+m_rear+1, newQueue+capacity-start);  
  24.         }  
  25.   
  26.         //重新設置指針位置:詳細信息請看下面圖解  
  27.         m_front = 2*capacity-1;  
  28.         m_rear = capacity-2;  
  29.         capacity *= 2;  
  30.   
  31.         delete []m_queue;  
  32.         m_queue = newQueue;  
  33.     }  
  34.   
  35.     //隊尾指針後移  
  36.     //注意:此處m_front+1可能需要回繞  
  37.     m_rear = (m_rear+1)%capacity;  
  38.     m_queue[m_rear] = item;  
  39. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. inline const Type &MyQueue<Type>::front() const  
  3. throw (std::range_error)  
  4. {  
  5.     if (isEmpty())  
  6.         throw range_error("queue is empty");  
  7.     //注意:此處m_front+1可能需要回繞  
  8.     return m_queue[(m_front+1)%capacity];  
  9. }  
  10.   
  11. template <typename Type>  
  12. inline const Type &MyQueue<Type>::rear() const  
  13. throw (std::range_error)  
  14. {  
  15.     if (isEmpty())  
  16.         throw range_error("queue is empty");  
  17.   
  18.     return m_queue[m_rear];  
  19. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. template <typename Type>  
  2. inline void MyQueue<Type>::pop()  
  3. throw (std::range_error)  
  4. {  
  5.     if (isEmpty())  
  6.         throw range_error("queue is empty");  
  7.   
  8.     //注意:此處m_front+1可能需要回繞  
  9.     m_front = (m_front+1)%capacity;  
  10.     m_queue[m_front].~Type();   //顯示調用析構函數以銷燬(析構)對象  
  11. }  
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //輸出隊列所有內容以做測試  
  2. template <typename Type>  
  3. ostream &operator<<(ostream &os, const MyQueue<Type> &queue)  
  4. {  
  5.     for (int i = (queue.m_front+1)%(queue.capacity);  
  6.             i <= queue.m_rear; /**空**/ )  
  7.     {  
  8.         os << queue.m_queue[i] << ' ';  
  9.         if (i == queue.m_rear)  
  10.             break;  
  11.         else  
  12.             i = (i+1)%(queue.capacity);  
  13.     }  
  14.   
  15.     return os;  
  16. }  

補充說明

當隊列已滿時的兩類擴充操作:


擴充之後的內存佈局:

 

 

附-測試代碼:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. int main()  
  2. {  
  3.     MyQueue<char> cQueue(3);  
  4.     cQueue.push('A');  
  5.     cQueue.push('B');  
  6.   
  7.     //因爲cQueue實際能夠用的大小爲2, 所以此處會對數組進行放大  
  8.     cQueue.push('C');  
  9.     cout << cQueue << endl;  
  10.     cout << "front = " << cQueue.front() << ", rear = "  
  11.          << cQueue.rear() << endl;  
  12.   
  13.     cQueue.pop();  
  14.     cQueue.pop();  
  15.     cQueue.push('D');  
  16.     cQueue.push('E');  
  17.     cQueue.push('F');  
  18.   
  19.     //此時queue的m_rear會進行迴繞  
  20.     cQueue.push('G');  
  21.     cQueue.pop();  
  22.     cQueue.push('H');  
  23.   
  24.     //此時隊列已滿, 再添加元素則會進行對隊列擴張  
  25.     //此時m_rear已經迴繞, 則會觸發兩次拷貝操作  
  26.     cQueue.push('I');  
  27.   
  28.     //驗證是否能夠正常工作  
  29.     cout << cQueue << endl;  
  30.     cout << "front = " << cQueue.front() << ", rear = "  
  31.          << cQueue.rear() << endl;  
  32.   
  33.     for (char ch = '1'; ch <= '9'; ++ch)  
  34.         cQueue.push(ch);  
  35.     for (int i = 0; i < 4; ++i)  
  36.         cQueue.pop();  
  37.   
  38.     cout << cQueue << endl;  
  39.     cout << "front = " << cQueue.front() << ", rear = "  
  40.          << cQueue.rear() << endl;  
  41.   
  42.     return 0;  
  43. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章