• 设计循环队列---力扣622


    1、题目

    1.1基础设置与讲解

    循环队列,即固定长度的队列,可以想象成一个环形队列

    就类似于这种队列,队尾指针后会有一个空位,用于控制判断队列为空还是为满;

    1. typedef int MyDataType;
    2. typedef struct {
    3. MyDataType front;
    4. MyDataType rear;
    5. MyDataType* a;
    6. MyDataType capacity;
    7. } MyCircularQueue;

    首先将int更名为MyDataType,方便对数据类型的统一管理;

    并定义了一个结构体MyCircularQueue,

    结构体内的成员

        MyDataType front;-----用于表示队列头指针
        MyDataType rear;-----尾指针
        MyDataType* a;-----存储队列元素的数组
        MyDataType capacity;-----队列所占空间大小

    1.2节点创建

    1. MyCircularQueue* myCircularQueueCreate(int k) {
    2. MyCircularQueue* new = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    3. if (new == NULL)
    4. {
    5. perror("malloc fail");
    6. exit(-1);
    7. }
    8. new->a = (int*)malloc(sizeof(int)*(k+1));
    9. new->front = new->rear = 0;
    10. new->capacity = k ;
    11. return new;
    12. }

    首先节点的创建必须有返回值,以供使用,这个返回值显而易见应该是MyCircularQueue*,因为返回的是一个结构体的存储。

    先开辟一个大小为MyCircularQueue类型的空间,并且创建一个新结构体用于接收,对开皮的空间进行判定,如果开辟失败则返回perror,并结束进行,如果空间开辟成功,则将结构体内部成员进行初始化,防止出现野指针、空指针等问题。

    其中capacity的大小为k,而

        new->a = (int*)malloc(sizeof(int)*(k+1));

    此处的k是队列长度,而开辟空间的时候应该加一,因为使用a这个数组存储,而数组是从0开始编号,所以k+1;

    1.3队列的满和空

    1. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    2. return obj->front == obj->rear;
    3. }
    4. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    5. return (obj->rear + 1) % (obj->capacity+1) == obj->front;
    6. }

    第一个函数,判断队列是否为空,为空则返回true,如果不为空则返回false,当头指针与尾指针相同时,则代表队列此时为空,如下图;

    第二个函数,判断队列是否为满,为满则返回true,如果不为满则返回false,

    1.4入队列

    1. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    2. if (myCircularQueueIsFull(obj))
    3. {
    4. return false;
    5. }
    6. obj->a[obj->rear] = value;
    7. obj->rear++;
    8. obj->rear = (obj->rear) % (obj->capacity+1);
    9. return true;
    10. }

    首先保证队列不为满,为满则无法进行存储;队尾指针进行数组数据输入。

     obj->rear = (obj->rear) % (obj->capacity+1);

    这行代码的作用是将 rear 限制在 0 到 obj->capacity(包含)之间,以确保其不超过队列的容量。

    其中如果只是obj->capacity的话只能在0~capacity-1,所以需要obj->capacity+1;

    这是因为循环队列是一个环形结构,在到达数组末尾时会绕回到数组的起始位置,因此需要用取模操作来实现这种循环。

    1.5出队列

    1. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    2. if (myCircularQueueIsEmpty(obj))
    3. {
    4. return false;
    5. }
    6. obj->a[obj->front] = 0;
    7. obj->front++;
    8. obj->front = (obj->front) % (obj->capacity+1);
    9. return true;
    10. }

    1.6取头指针和尾指针的数据

    1. int myCircularQueueFront(MyCircularQueue* obj) {
    2. if (myCircularQueueIsEmpty(obj))
    3. {
    4. return -1;
    5. }
    6. return obj->a[obj->front];
    7. }
    8. int myCircularQueueRear(MyCircularQueue* obj) {
    9. if (myCircularQueueIsEmpty(obj))
    10. {
    11. return -1;
    12. }
    13. return obj->a[(obj->rear+obj->capacity)%(obj->capacity+1)];
    14. }

    1.7对队列进行释放

    1. void myCircularQueueFree(MyCircularQueue* obj) {
    2. while (obj->a[obj->front] != 0)
    3. {
    4. obj->a[obj->front] = 0;
    5. obj->front++;
    6. obj->front = (obj->front) % (obj->capacity+1);
    7. }
    8. obj->capacity = 0;
    9. obj->front = obj->rear = NULL;
    10. free(obj->a);
    11. free(obj);
    12. }

    2、总结

    1. 选择合适的数据结构: 循环队列通常基于数组实现,因为数组能够提供 O(1) 的随机访问时间,这对于循环队列中常见的插入和删除操作至关重要。但也可以使用链表实现循环队列,尤其在需要动态扩展队列大小时,链表实现可能更灵活。

    2. 确定队列的容量: 循环队列的容量应该是队列数组的大小减去 1,这是因为需要一个额外的位置来区分队列的空和满状态。

    3. 定义队列的属性: 需要定义队列结构体,并在其中包含头指针、尾指针以及存储元素的数组等属性。头指针和尾指针用于指示队列的起始位置和结束位置,而数组用于存储队列中的元素。

    4. 实现基本操作: 循环队列通常需要实现以下基本操作:

      • 入队操作(enqueue):向队尾插入元素。
      • 出队操作(dequeue):从队首删除元素。
      • 判空操作(isEmpty):检查队列是否为空。
      • 判满操作(isFull):检查队列是否已满。
      • 获取队首元素(front):返回队首元素的值,但不删除它。
      • 获取队尾元素(rear):返回队尾元素的值,但不删除它。

  • 相关阅读:
    AT89S52单片机
    【C++11】Lambda 表达式:基本使用 和 底层原理
    vue3前端开发-flex布局篇
    iText生成PDF文件
    com.google.code:kaptcha-2.3.jar
    CSS常用选择器用法
    【linux API分析】module_init
    万能在线答题考试小程序源码系统 既能刷题 又能考试 带完整的搭建教程
    SkyWalking 本地启动以及闪退问题
    C#的预处理指令
  • 原文地址:https://blog.csdn.net/ykcyk/article/details/139401759