循环队列实现
循环队列的实现
队列的定义:队列是一种特殊的线性表,它只支持队头元素的弹出与队尾元素的压入。对于队列内的任何元素我们不能进行遍历、修改。
虽然我们定义的是一个循环队列,但是其基本性质不会因为循环发生改变。
队列底层数据类型可以使用顺序表来保存也可以使用链表来保存。这里我们采用的是顺序表来作为底层数据类型。
定义如下代码
template<typename T>
class Queue
{
public:
// 入队
void push(T data);
// 出队
void pop();
public:
Queue();
~Queue();
private:
T* arr;
// 队首索引
int front;
// 队尾索引
int rear;
};
因为队列是一种特殊的线性表,故我们可以使用一个指针保存队列的首地址来保存所有的队列元素。
队列的初始化#
循环队列的初始化与非循环队列的区别在初始化并无区别,只是在元素的添加和元素的释放存在区别。
template<typename T>
inline Queue<T>::Queue()
{
// #define MAXQSIZE = 100
this->arr = new T[MAXQSIZE];// 创建一个大小为100的数组
this->front = 0;
this->rear = 0;
}
队列的操作#
入队
循环队列实现的一个重要基础就是余数的概念,通过余数我们可以把一个数牢牢地限制在 [ 0 ~ MAXQSIZE )之间,如此在rear不断增加时,arr[rear]始终不会访问越界。
通过余数的概念我们就可以将rear的范围限制,但是此时的rear虽然不会越界但是有可能会覆盖我们原本存在的值,那么对于这种情况我们应该怎么解决?此时的数组又是一种什么状态?
这里我们就要使用front索引了,为了不让rear覆盖我们之前定义的值,我们只需要提前判断rear+1之后是否等于front,如果等于就代表该数组已经满了不能在向里面添加数据了,如果不等于front那么就意味着数组依然存在可以使用的空间。
template<typename T>
inline void Queue<T>::push(data_type data)
{
// 入队时
// rear 增加 ===> 假溢出 ===> 真溢出
// 真溢出
if((rear + 1) % MAXQSIZE == front){
return;
}
// 假溢出
else {
arr[rear] = data;
++rear;
}
}
这里可能会有人疑问,为什么一定是front,front代表的含义是什么?为什么不能是等于0就不在添加了?而解决这些疑问就是实现循环队列的小秘诀了。为了探究front的含义下面我们来看一下循环队列的pop函数。
出队
我们发现这里我们并未对元素执行删除操作,仅仅只是简单的将front的值向后移动一位,注意这里同样使用余数的概念将front限制在 [ 0 ~ MAXQSIZE ) 之间。
如果我们的程序按照这样的逻辑去执行多次push后,在进行pop我们应该能发现front在向rear靠近的同时,front之前的部分,即被弹出的部分是可以被rear覆盖的。
template<typename T>
inline void Queue<T>::pop()
{
if(front == rear) return;
// 释放队头时, 可能对头达到队列最大位置,但并未到达队尾
front = (front + 1) % MAXQSIZE;
}
总结#
通过这样对索引范围的限制使得整个数组仿佛一个循环,节约插入、删除的时间复杂度,提高内存使用效率。
求余数的作用:限制下标范围
真溢出的判定:( rear + 1 ) % MAXQSIZE == front
真队尾的判定:front == rear
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异