C语言循环队列实现:Push、Pop与查询操作详解

纸上星河
纸上星河
发布时间:2026-10-13 | 3人浏览过
原创

在C语言编程中,循环队列是解决固定大小缓冲区数据流转的高效数据结构。本文基于典型算法题需求,展示如何使用结构体管理队头、队尾指针及计数变量,利用取模运算实现数组首尾相接的循环逻辑,并提供完整的入队、出队、状态判断及元素查询的代码实现。

结果或目标

本方案旨在实现一个支持以下五种操作的循环队列模块,适用于需要限制最大容量且频繁进行首尾进出的场景:

  • Push K:将整数 K 插入队尾。
  • Pop:移除队头元素。
  • Query K:获取队列中第 K 个位置的元素(1-based)。
  • Isempty:判断队列是否为空。
  • Isfull:判断队列是否已满。

准备条件

实现循环队列需要预先定义存储数据的数组空间,并维护三个关键状态变量以克服普通数组队列“假溢出”的问题:

  1. 数组空间:data[queuesize],用于实际存储元素。需注意数组索引从 0 开始,而题目要求的 Query K 为第 K 个元素(通常指逻辑位置),需在访问时转换为数组索引。
  2. front:指向队头元素的索引,初始为 0。
  3. rear:指向队尾下一个空闲位置的索引,初始为 0。这种设计使得队列空时 front == rear,队列满时可以通过额外的 count 变量或预留一个空位来区分。
  4. 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; i 结束后调用 free(Q),或将结构体定义为栈上变量(若 size 允许)以避免堆分配开销。 2. Query 逻辑简化:原代码中 QueueSearch 函数包含大量关于 front 和 rear 大小关系的比较,这是早期手动模拟循环队列的思路。实际上,使用取模运算 (front + offset) % m 可以统一处理所有跨环情况,显著降低代码复杂度并减少出错概率。

简要总结

通过使用 front、rear 和 count 三变量配合取模运算,C 语言可以高效地构建固定大小的循环队列。关键在于正确计算数组索引以及准确判断空/满状态。以上就是C语言循环队列实现的详细内容,更多关于数据结构与算法优化的资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表