C语言循环队列实现:Push、Pop与查询操作详解
在C语言编程中,循环队列是解决固定大小缓冲区数据流转的高效数据结构。本文基于典型算法题需求,展示如何使用结构体管理队头、队尾指针及计数变量,利用取模运算实现数组首尾相接的循环逻辑,并提供完整的入队、出队、状态判断及元素查询的代码实现。
结果或目标
本方案旨在实现一个支持以下五种操作的循环队列模块,适用于需要限制最大容量且频繁进行首尾进出的场景:
Push K:将整数K插入队尾。Pop:移除队头元素。Query K:获取队列中第K个位置的元素(1-based)。Isempty:判断队列是否为空。Isfull:判断队列是否已满。
准备条件
实现循环队列需要预先定义存储数据的数组空间,并维护三个关键状态变量以克服普通数组队列“假溢出”的问题:
- 数组空间:
data[queuesize],用于实际存储元素。需注意数组索引从 0 开始,而题目要求的Query K为第K个元素(通常指逻辑位置),需在访问时转换为数组索引。 front:指向队头元素的索引,初始为 0。rear:指向队尾下一个空闲位置的索引,初始为 0。这种设计使得队列空时front == rear,队列满时可以通过额外的count变量或预留一个空位来区分。count:记录当前队列中的元素个数,用于快速判断空/满状态,避免复杂的指针计算歧义。
真实操作顺序
第1步:定义结构与初始化
目的:建立数据存储框架并确保状态归零。
#define queuesize 100001 // 根据题目约束设定最大值
struct queue {
int front; // 队头索引
int rear; // 队尾索引
int data[queuesize]; // 数据存储区
int count; // 当前元素计数
};
// 初始化函数
void InitQueue(struct queue *Q) {
Q->front = 0;
Q->rear = 0;
Q->count = 0;
}
入口:程序启动或创建新队列实例时调用。
重要参数:Q 为指向队列结构的指针。
完成状态:front、rear 和 count 均重置为 0。
第2步:实现 Push(入队)
目的:在队列未满时将元素添加至 rear 指向的位置,并移动 rear。
void EnQueue(struct queue *Q, int element, int m) {
// 如果队列已满,输出 failed
if (Q->count == m) {
printf("failed\n");
return;
}
// 将元素存入 rear 位置
Q->data[Q->rear] = element;
// 更新状态:计数加一,rear 向前移动一位(取模实现循环)
Q->count++;
Q->rear = (Q->rear + 1) % m;
}
入口:处理 Push K 指令。
动作:检查容量 → 写入数据 → 更新指针。
注意:使用 % m 确保 rear 在达到数组末尾后回到 0。
第3步:实现 Pop(出队)
目的:移除队头元素,并移动 front。
void Dequeue(struct queue *Q, int m) {
// 如果队列为空,输出 failed
if (Q->count == 0) {
printf("failed\n");
return;
}
// 无需取出具体值即可视为出队,只需移动指针
// 若需获取值可赋值给局部变量 element = Q->data[Q->front];
// 更新状态:计数减一,front 向前移动一位(取模)
Q->count--;
Q->front = (Q->front + 1) % m;
}
入口:处理 Pop 指令。
动作:检查非空 → 移动指针。
完成状态:front 指向新的队头,count 减少。
第4步:实现 Query(查找第K个元素)
目的:根据逻辑索引 K 定位并输出对应的数组元素。
逻辑分析:
在循环队列中,第 K 个元素(1-based)相对于队头 front 的偏移量是 K-1。
因此,目标数组索引 index = (front + K - 1) % m。
void QueueSearch(struct queue *Q, int k, int m) {
// 合法性检查:队列不能为空,且 K 必须在有效范围内 [1, m]
// 更严格的检查是 K <= current_count,但题目示例暗示可能允许查空或越界返回 failed
// 根据样例 Query 3 在容量2时失败,说明 K 不能超过队列总容量 M,且通常不应超过当前长度
// 原代码逻辑稍显复杂,此处优化为标准的索引计算公式
if (Q->count == 0 || k < 1 || k > Q->count) {
printf("failed\n");
return;
}
// 计算实际数组下标
int index = (Q->front + k - 1) % m;
printf("%d\n", Q->data[index]);
}
注:原始草稿中的 Query 逻辑使用了大量的 if-else 分支处理 front/rear 的相对位置,这在现代 C 编程中是不必要的。只要使用正确的取模偏移公式 (front + k - 1) % m,无论 front 是否绕回,都能正确获取第 K 个元素的值。上述代码块采用了更简洁且符合逻辑的标准写法。若需严格复刻原题 AC 代码的逻辑路径,请参考下方保留的原始复杂逻辑版本(见 review_notes)。
第5步:状态判断 (Isempty / Isfull)
目的:反馈队列当前的占用情况。
// 判断空
if (Q->count == 0) {
printf("yes\n"); // 或 true
} else {
printf("no\n");
}
// 判断满
if (Q->count == m) {
printf("yes\n"); // 或 true
} else {
printf("no\n");
}
入口:处理 Isempty 和 Isfull 指令。
参数:m 为队列最大容量。
完成判断
程序应能持续读取 N 条指令并在每次操作后立即输出结果(如 failed, yes, no 或数值)。在主函数中,通过 while(scanf(...)) 循环处理多组测试用例,每组数据开始前需重新初始化队列结构或分配新内存。
原文有依据的排错
1. 内存泄漏风险:原始代码中使用 malloc 分配队列结构,但在循环外部未释放。在实际长期运行的应用中,应在每轮 for(i=0; ifree(Q),或将结构体定义为栈上变量(若 size 允许)以避免堆分配开销。
2. Query 逻辑简化:原代码中 QueueSearch 函数包含大量关于 front 和 rear 大小关系的比较,这是早期手动模拟循环队列的思路。实际上,使用取模运算 (front + offset) % m 可以统一处理所有跨环情况,显著降低代码复杂度并减少出错概率。
简要总结
通过使用 front、rear 和 count 三变量配合取模运算,C 语言可以高效地构建固定大小的循环队列。关键在于正确计算数组索引以及准确判断空/满状态。以上就是C语言循环队列实现的详细内容,更多关于数据结构与算法优化的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













