C语言数据结构入门——队列补充:循环队列的数组实现

文章目录

  • 前言
  • 一、概念
  • 二、代码实现
    • [1. 循环队列结构](#1. 循环队列结构)
    • [2. 创建循环链表(初始化)](#2. 创建循环链表(初始化))
      • [3. 判断队列是否满了](#3. 判断队列是否满了)
      • [4. 判断队列是否为空](#4. 判断队列是否为空)
      • [5. 往队列中插入数据](#5. 往队列中插入数据)
      • [6. 从队列中删除数据](#6. 从队列中删除数据)
      • [7. 返回队头元素](#7. 返回队头元素)
      • [8. 返回队尾元素](#8. 返回队尾元素)
      • [9. 销毁队列](#9. 销毁队列)
  • 总结

前言

在上一篇中,我们实现了基于链表的普通队列。本篇作为队列章节的补充,介绍一种更高效的变体------循环队列。它通过首尾相连的环形结构复用出队后的空间,并采用"多申请一个位置"的技巧区分队空与队满。本文将从结构定义入手,逐步实现创建、入队、出队、取队头/队尾、判空、判满及销毁等接口,帮助你进一步完善队列知识体系。


一、概念

实际中还有一种特殊的队列叫循环队列,环形队列首尾相连成环,环形队列可以使用数组实现,也可以使用循环链表实现

本文用数组来实现循环队列


二、代码实现

1. 循环队列结构

c 复制代码
typedef struct {
    //存储整型类型的数据
    int* arr; //存储整型数据的动态数组指针
    int front; //对头下标
    int rear; //队尾下标
    int capacity; //循环队列空间大小

} MyCircularQueue;
    1. arr作是存储整型数据的动态数组指针,作为循环队列的元素存储数组
    1. front和rear分别为队头下标 和队尾下标,用于实时标记队头队尾的位置
    1. capacity为循环队列的空间大小,不过我们每次申请空间要申请 (capacity+1)个,方便后续操作

2. 创建循环链表(初始化)

c 复制代码
MyCircularQueue* myCircularQueueCreate(int k) {
    //创建循环队列,并向操作系统申请空间
    MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if (pq == NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    //给存储整型数组的指针分配空间,申请k+1个空间
    pq->arr = (int*)malloc(sizeof(int) * (k + 1));
    if (pq->arr == NULL)
    {
        perror("malloc fail");
        free(pq);
        return NULL;
    }
    //初始情况下头下标和尾下标都为0
    pq->front = pq->rear = 0;
    //capacity为k
    pq->capacity = k;

    return pq;
}
    1. 先向操作系统申请循环队列这个结构体pq的内存空间, MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    1. 再给循环队列的储存数组arr申请(k+1)个int大小的空间,
    1. 初始情况下头下标和尾下标都为0,capacity为k

申请k+1个空间的原因:

申请 k + 1 k+1 k+1 个空间是为了空出一个闲置位置,以此区分"队空"和"队满"的状态 。如果不额外多开辟一个位置,当队列全满(存了 k k k 个元素)和队列全空(存了 0 0 0 个元素)时,队头与队尾指针都会重合指向同一位置(即 front == rear),程序将无法仅凭下标判断此时究竟是满还是空;而多开辟一个空间后,约定保留一个空位不存数据,就能清晰地区分:front == rear 时为"队空",而 (rear + 1) % (k + 1) == front 时为"队满"。

3. 判断队列是否满了

c 复制代码
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //如果rear往后再走一步就是front,就说明队列满了
    return (obj->rear + 1) % (obj->capacity + 1) == (obj->front);
}
  • 如果rear往后再走一步就是front,就说明队列满了

4. 判断队列是否为空

c 复制代码
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //队尾等于队头
    return obj->rear == obj->front;
}
  • 当队尾等于对头,说明队列为空

5. 往队列中插入数据

c 复制代码
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //往循环队列插入数据

    //如果队列满了,则不能插入数据
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->arr[obj->rear] = value;
    obj->rear++;
    //防止rear越界
    obj->rear %= (obj->capacity + 1);
    return true;

}
    1. 先判断队列是否满了,若满了,则不能插入
    1. 若未满,在rear位置插入数据,并让rear++,往后移动一位
    1. 此时要防止rear越界,用obj->rear %= (obj->capacity + 1)操作保障rear不越界
    1. 成功插入后,返回true

6. 从队列中删除数据

c 复制代码
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //判断循环队列是否为空
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //不为空
    obj->front++;
    //防止front越界
    obj->front %= (obj->capacity + 1);
    return true;
}
    1. 先判断队列是否为空
    1. 不为空则让front++,表示删除原front指向的元素
    1. obj->front %= (obj->capacity + 1)防止front越界

7. 返回队头元素

c 复制代码
int myCircularQueueFront(MyCircularQueue* obj) {
    //判断队列是否为空
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arr[obj->front];
}
    1. 向判断是否为空队列
    1. 不为空队列则返回下标为front的元素

8. 返回队尾元素

c 复制代码
int myCircularQueueRear(MyCircularQueue* obj) {
    //判断队列是否为空
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    int prev = obj->rear - 1;
    //如果rear是指向的是下标为0的位置
    if (obj->rear == 0)
    {
        prev = obj->capacity;
    }
    return obj->arr[prev];
}
    1. 先检查不为空队列
    1. 因为rear始终指向的是队尾后一个位置,所以真正队尾元素下标为rear-1,创建prev来接收真正队尾元素下标
    1. 返回下标为prev的元素

9. 销毁队列

c 复制代码
void myCircularQueueFree(MyCircularQueue* obj) {
    //如果obj->arr不为空,则free(arr)
    if (obj->arr)
        free(obj->arr);
    free(obj);
    obj = NULL;
}
    1. 如果obj->arr不为空,则free(arr)
    1. 最后释放obj,并将obj置为NULL

总结

以上就是本篇补充的全部内容。本文在链式队列的基础上,介绍了循环队列的数组实现,重点讲解了如何通过申请 k+1 个空间并利用取模运算来区分队空与队满。详细实现了创建、判满、判空、入队、出队、取队头、取队尾及销毁等接口。掌握循环队列,能让你对队列的空间管理与环形缓冲思想有更深入的理解。

相关推荐
志尊宝1 小时前
Vue3 零基础每日笔记(081):富文本编辑器集成 wangEditor 5——封装
前端·javascript·vue.js·笔记·信息可视化·echarts
源图客1 小时前
Python环境安装与PyCharm的第一个项目
开发语言·python·pycharm
zx_741484811 小时前
PyQt5 环境搭建保姆级教程:PyCharm + QtDesigner + PyUIC 完整配置
开发语言·qt·pycharm·pyqt
志尊宝1 小时前
Vue3 零基础每日笔记(080):Vue3 + ECharts 数据可视化——封装可复用图表组件
前端·javascript·vue.js·笔记·信息可视化·echarts
爱吃提升1 小时前
Dev-C++ 5.11 完整版快捷键大全(信奥/初学适用)
c语言·c++
Soari1 小时前
交换机转发模式
开发语言·网络·php
沪上企服通2 小时前
从票据采集到凭证生成:代账 pipeline 的 OCR、查重与规则引擎设计笔记
笔记·ocr
careathers2 小时前
【数据结构】二叉树拓展
数据结构·算法
deviant-ART2 小时前
Java 并发编程:深入理解 synchronized 的使用与原理
java·开发语言·后端