一、什么是循环队列
队列是一种“先进先出”(FIFO)的线性表。普通的顺序队列在使用数组实现时,随着出队操作,front 指针不断后移,数组前端的空间会被浪费,产生“假溢出”现象。
循环队列则通过将数组在逻辑上看成一个环,利用取模运算让 rear 和 front 指针能够循环移动,从而复用数组空间,解决了假溢出的问题。
本文基于一个最大容量为 10 的循环队列,采用浪费一个数组单元的方式区分队空和队满,并编写了可循环操作的菜单程序,方便大家直观地验证队列的各种状态。
二、数据结构定义
首先定义队列的结构体,包含一个固定大小的数组和两个整型指针 front 与 rear。
#define Maxsize 10
typedef struct Queue {
int data[Maxsize];
int front, rear;
} Queue;
-
Maxsize设置为 10,意味着数组最多存放 9 个有效元素(因为需要浪费一个位置)。 -
front指向队头元素的前一个位置。 -
rear指向队尾元素。
这种设计下,初始时 front 和 rear 都设为 0,队列为空。
三、核心功能函数实现
1. 判断队空与队满
队空条件:front == rear
队满条件:(rear + 1) % Maxsize == front
// 判断队满
bool isSuperfluous(Queue q) {
return (q.rear + 1) % Maxsize == q.front;
}
// 判断队空
bool isKong(Queue q) {
return q.front == q.rear;
}
注:函数名 isSuperfluous 和 isKong 是本文为了教学特意保留的风格,实际开发中建议使用 isFull 和 isEmpty 这种更规范的命名。
2. 入队操作(带判满保护)
入队时先检查队列是否已满,满则拒绝入队;未满则先移动 rear 指针,再将元素放入新的 rear 位置。
bool Insert(Queue &q, int e) {
if (isSuperfluous(q)) {
cout << "队满,无法入队!" << endl;
return false;
}
q.rear = (q.rear + 1) % Maxsize; // 先移动 rear
q.data[q.rear] = e; // 再存值
cout << "元素 " << e << " 入队成功" << endl;
return true;
}
关键点:(q.rear + 1) % Maxsize 保证 rear 能循环回到数组开头,不会越界。
3. 出队操作(带判空保护)
出队时先判断队列是否为空,空则拒绝出队;非空则先移动 front 指针,再取出 front 所指的元素。
bool Pop(Queue &q, int &e) {
if (isKong(q)) {
cout << "队空,无法出队!" << endl;
return false;
}
q.front = (q.front + 1) % Maxsize; // 先移动 front
e = q.data[q.front]; // 再取值
return true;
}
这里将出队元素通过引用参数 e 返回,函数本身的布尔返回值用于表示操作是否成功。
四、可循环执行的交互菜单
为了让代码能够连续操作,而不是只执行一次就退出,我们在 main() 中使用了 while(true) 循环,并提供了几个选项:
int main() {
Queue q;
q.front = 0;
q.rear = 0;
int op;
while (true) {
cout << "\n======== 循环队列菜单 ========" << endl;
cout << "1. 入队" << endl;
cout << "2. 出队" << endl;
cout << "3. 判断队满" << endl;
cout << "4. 判断队空" << endl;
cout << "0. 退出" << endl;
cout << "请输入操作码: ";
cin >> op;
if (op == 0) {
cout << "程序退出。" << endl;
break;
}
switch (op) {
case 1: {
int a;
cout << "请输入要入队的元素: ";
cin >> a;
Insert(q, a);
break;
}
case 2: {
int e;
if (Pop(q, e)) {
cout << "出队的元素为: " << e << endl;
}
break;
}
case 3: {
if (isSuperfluous(q))
cout << "队满" << endl;
else
cout << "队没有满" << endl;
break;
}
case 4: {
if (isKong(q))
cout << "队空" << endl;
else
cout << "队不空" << endl;
break;
}
default:
cout << "无效操作码,请重新输入" << endl;
}
}
return 0;
}
通过这个菜单,我们可以反复进行入队、出队、查询状态等操作,非常方便用来测试队列的各种边界情况。例如:连续入队 9 个元素直到队满,再尝试入队;或者全部出队后再尝试出队,观察程序的保护提示。
五、完整代码
将上述所有部分整合在一起,就是一套可以直接编译运行的 C++ 程序:
#include <bits/stdc++.h>
using namespace std;
#define Maxsize 10
// 循环队列数据结构
typedef struct Queue {
int data[Maxsize];
int front, rear;
} Queue;
// 判断队满
bool isSuperfluous(Queue q) {
return (q.rear + 1) % Maxsize == q.front;
}
// 判断队空
bool isKong(Queue q) {
return q.front == q.rear;
}
// 入队(带判满保护)
bool Insert(Queue &q, int e) {
if (isSuperfluous(q)) {
cout << "队满,无法入队!" << endl;
return false;
}
q.rear = (q.rear + 1) % Maxsize;
q.data[q.rear] = e;
cout << "元素 " << e << " 入队成功" << endl;
return true;
}
// 出队(带判空保护)
bool Pop(Queue &q, int &e) {
if (isKong(q)) {
cout << "队空,无法出队!" << endl;
return false;
}
q.front = (q.front + 1) % Maxsize;
e = q.data[q.front];
return true;
}
int main() {
Queue q;
q.front = 0;
q.rear = 0;
int op;
while (true) {
cout << "\n======== 循环队列菜单 ========" << endl;
cout << "1. 入队" << endl;
cout << "2. 出队" << endl;
cout << "3. 判断队满" << endl;
cout << "4. 判断队空" << endl;
cout << "0. 退出" << endl;
cout << "请输入操作码: ";
cin >> op;
if (op == 0) {
cout << "程序退出。" << endl;
break;
}
switch (op) {
case 1: {
int a;
cout << "请输入要入队的元素: ";
cin >> a;
Insert(q, a);
break;
}
case 2: {
int e;
if (Pop(q, e)) {
cout << "出队的元素为: " << e << endl;
}
break;
}
case 3: {
if (isSuperfluous(q))
cout << "队满" << endl;
else
cout << "队没有满" << endl;
break;
}
case 4: {
if (isKong(q))
cout << "队空" << endl;
else
cout << "队不空" << endl;
break;
}
default:
cout << "无效操作码,请重新输入" << endl;
}
}
return 0;
}
六、注意事项与常见问题
-
浪费一个空间的问题
本例使用“队满条件是(rear + 1) % Maxsize == front”的方式来区分队空和队满,因此实际可用的最大元素个数为Maxsize - 1(即 9 个)。若不想浪费,可以引入一个标志位(如size)来记录当前元素个数。 -
指针移动顺序
入队和出队的指针移动顺序必须严格遵守“先移动再存取”的原则。顺序一旦颠倒,会导致第一个元素丢失或读取到错误位置。 -
边界保护
本代码对入队和出队都增加了判满/判空保护,这是非常重要的。如果不加保护,队满入队会覆盖队头元素,队空出队会读取到脏数据。 -
命名习惯
文中函数名isSuperfluous和isKong虽然比较有趣,但在实际项目中还是推荐使用isFull和isEmpty,以提高代码的可读性。
七、总结
循环队列是数据结构入门的必修课,通过这篇博客,我们实现了一个功能完整的循环队列,并配上了一个循环交互菜单来方便测试。代码逻辑清晰,边界处理到位,非常适合刚接触队列的同学对照着敲一遍。
希望这篇文章对你理解循环队列有所帮助。如果觉得有用,欢迎点赞、收藏、关注~ 我们下篇再见!

3137

被折叠的 条评论
为什么被折叠?



