数据结构基础(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. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值