【数据结构】如何设计循环队列?图文解析(LeetCode)

news2025/1/12 4:06:17

LeetCode链接:622. 设计循环队列 - 力扣(LeetCode)

目录

做题思路

只开辟 k 个空间

多开一个空间

代码实现

1. 循环队列的结构

2. 开辟空间

3. 判断空

4. 判断满

5. 队尾插入数据

6. 队头删除数据

7. 获取队头元素

8. 获取队尾元素

9. 销毁队列

全部代码


做题思路

  • 设计循环队列,使用数组或链表都可以,各有优劣
  • 本文使用数组实现
  • 本文使用C语言实现

假设队列长度 k = 4

多开一块空间(开辟 k+1 块空间)可以方便区分空和满

为什么?

举个栗子:

只开辟 k 个空间

如果只开辟 k 个空间(假设 k = 4):

  • front(队头)
  • rear(队尾)
  • front 和 rear 初始都为 0

如果插入一个数据呢?

front 不变,rear 向后移动,如下图:

理想的判断空条件:

  • 队头 == 队尾,队列为空
  • 队头 != 队尾,队列非空

目前看来似乎可以正常判断队列是否为空

那如果队列满了呢?

如上图所示,队列满了

rear 又回到了 front 的位置

现在怎么判断满呢?

rear == front 吗?

似乎和前面判断空冲突了

想判断必须要特殊处理:比如加一个变量 size 来记录队列中元素的个数

这种方法虽然可以,但是会让代码显得很臃肿,于是我选择了另一种方法:

多开一个空间

如果多开了一个空间(开辟 k+1 个空间):

  • 这里多开的空间是不存放数据的
  • 这块空间存在的意义就是为了方便区分空和满

下面我将演示一下如何判断:

判断空:

  • 队头 == 队尾,队列为空
  • 队头 != 队尾,队列非空

判断满:

  • 队尾的下一个 == 队头,队列已满
  • 队尾的下一个 != 队头,队列未满

如下图:

那么好,这里简单介绍了一下我的实现思路:

  • 使用C语言实现
  • 使用数组实现
  • 数组多开一块空间

接下来就是详细的实现方法


代码实现

本题要求:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

1. 循环队列的结构

//循环队列的结构
typedef struct
{
    int* a;     //一个数组
    int front;  //存放队头的下标
    int rear;   //存放队尾的下标
    int k;      //队列长度
} MyCircularQueue;

2. 开辟空间

//开辟空间并初始化
MyCircularQueue* myCircularQueueCreate(int k)
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个方便区分空和满
    obj->a = (int*)malloc(sizeof(int) * (k + 1));
    obj->front = obj->rear = 0;
    obj->k = k;
    return obj;
}

3. 判断空

前面说过了,队头等于队尾时,队列为空

//检测循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
    return obj->front == obj->rear;
}

4. 判断满

注意这里有一些坑点

前面说过:队尾+1等于队头时,队列为满

但是我用的是数组,不是链表队尾+1有可能会越界

如上图:rear+1 = 5,越界了

怎么办呢?可以采用取余数(%)的方法,即:(rear+1)%(k+1),让 rear+1 变回 0,达到循环的效果。

情况1:队尾+1越界了

(rear + 1) % (k + 1) = 0

front = 0

(rear + 1) % (k + 1) = front,说明循环队列已满。

情况2:队尾+1没越界

(rear + 1) % (k + 1) = 4

front = 0

(rear + 1) % (k + 1) != front,说明循环队列未满。

//检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
    return (obj->rear + 1) % (obj->k + 1) == obj->front;
}

5. 队尾插入数据

这里需要一些操作,还是越界的问题,插入数据后队尾可能会越界,如下图:

rear 处插入数据后,rear 需要向后移动,导致越界

1)判断队列是否已满,满了是不能插入的,直接返回 false

2)在队尾插入数据

3)队尾++,可能会越界

4)依旧采用取余数的方法让队列循环起来,即:rear = rear % (k + 1)

//向循环队列插入一个元素。如果成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
    if (myCircularQueueIsFull(obj))
        return false;
    obj->a[obj->rear] = value;
    obj->rear++;
    obj->rear %= (obj->k + 1);
    return true;
}

6. 队头删除数据

删除数据后,队头也可能会有越界的情况,如下图:

删除 front 处的数据后,front 需要向后移动,导致越界

1)判断队列是否为空,空队列是不能删数据的,直接返回 false

2)删除数据很简单,直接让队头++即可

3)取余数避免越界:front = front % (k + 1)

//从循环队列中删除一个元素。如果成功删除则返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return false;
    obj->front++;
    obj->front %= (obj->k + 1);
    return true;
}

7. 获取队头元素

情况1:队列为空

根据题意,返回 -1

情况2:队列不为空

直接返回数组中下标为 front 的元素即可

//从队首获取元素。如果队列为空,返回-1
int myCircularQueueFront(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[obj->front];
}

8. 获取队尾元素

情况1:队列为空

根据题意,返回 -1

情况2:队列不为空

  • 需要返回数组中下标 rear 的前一个元素(rear-1
  • 此操作可能会导致越界
  • 用取余数的方式避免越界:(rear + k) % (k + 1)

如上图:这种情况下强行获取 rear 的前一个元素会导致越界

//获取队尾元素。如果队列为空,返回-1
int myCircularQueueRear(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}

9. 销毁队列

销毁所有动态开辟的空间

//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj)
{
    free(obj->a);
    free(obj);
}

全部代码

//循环队列的结构
typedef struct
{
    int* a;     //一个数组
    int front;  //存放队头的下标
    int rear;   //存放队尾的下标
    int k;      //队列长度
} MyCircularQueue;

//开辟空间并初始化
MyCircularQueue* myCircularQueueCreate(int k)
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个方便区分空和满
    obj->a = (int*)malloc(sizeof(int) * (k + 1));
    obj->front = obj->rear = 0;
    obj->k = k;
    return obj;
}

//检测循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
    return obj->front == obj->rear;
}

//检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
    return (obj->rear + 1) % (obj->k + 1) == obj->front;
}

//向循环队列插入一个元素。如果成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
    if (myCircularQueueIsFull(obj))
        return false;
    obj->a[obj->rear] = value;
    obj->rear++;
    obj->rear %= (obj->k + 1);
    return true;
}

//从循环队列中删除一个元素。如果成功删除则返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return false;
    obj->front++;
    obj->front %= (obj->k + 1);
    return true;
}

//从队首获取元素。如果队列为空,返回-1
int myCircularQueueFront(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[obj->front];
}

//获取队尾元素。如果队列为空,返回-1
int myCircularQueueRear(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}

//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj)
{
    free(obj->a);
    free(obj);
}

本文完

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/969138.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ElasticSearch第二讲:ES详解 - ElasticSearch基础概念

ElasticSearch第二讲:ES详解 - ElasticSearch基础概念 在学习ElasticSearch之前,先简单了解下ES流行度,使用背景,以及相关概念等。本文是ElasticSearch第二讲,ElasticSearch的基础概念。 文章目录 ElasticSearch第二讲…

【GoldenDict】win11牛津高阶英汉双解词典安装使用方法

【词典资源】 1(本文章使用的版本)牛津高阶(第10版 英汉双解) V11.8: https://pan.baidu.com/s/11272Cldde_2UttQkWS2MlQ 提取码:0p3j 2(另一版本)第十版 v13.2: ht…

信息系统项目管理师(第四版)教材精读思维导图-第九章项目范围管理

请参阅我的另一篇文章,综合介绍软考高项: 信息系统项目管理师(软考高项)备考总结_计算机技术与软件专业技术_铭记北宸的博客-CSDN博客 本章思维导图PDF格式 本章思维导图XMind源文件 目录 9.1 管理基础 9.2 管理过程 9.3 规划范…

【Linux】线程安全-信号量

文章目录 信号量原理信号量保证同步和互斥的原理探究信号量相关函数初始化信号量函数等待信号量函数释放信号量函数销毁信号量函数 信号量实现生产者消费者模型 信号量原理 信号量的原理:资源计数器 PCB等待队列 函数接口 资源计数器:对共享资源的计…

Python之单调栈

单调栈 了解单调栈先要了解栈。栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈&…

用JAVA(springboot) 开发的彩票模拟系统

闲暇时间,自己写了一个模拟彩票系统,里面研究了开奖算法,下单算法,彩票的各种计算规则。需要源码的私信,研究为主,切勿商务用途。

2022年06月 C/C++(七级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;有多少种二叉树 输入n(1<n<13)&#xff0c;求n个结点的二叉树有多少种形态 时间限制&#xff1a;1000 内存限制&#xff1a;65536 输入 整数n 输出 答案 样例输入 3 样例输出 5 这个问题可以使用…

外部中断(EXTI) - 按键控制LED

一、外部中断/事件控制器(EXTI)结构图 1、结构图分析 外部中断主要由外部中断/事件控制器(External interrupt/event controller, EXTI)控制&#xff0c;它管理了外部中断或者事件的使能与否、触发方式等功能。 &#xff08; 外部中断/事件控制器(EXTI)结构图 &#xff09; …

记录错误:Access denied for user ‘root‘@‘localhost‘ (using password:No) 解决方案

他说我没输入密码&#xff0c;但是我输入了啊&#xff1f;&#xff1f;于是&#xff0c;我试了试这儿&#xff0c;password 一改就好了。。。 他原来是是我打的很快&#xff0c;快速生成的。。。。

ESP32之LEDC(PWM信号的输出)

一、PWM信号简介 PWM&#xff1a;脉冲宽度调制&#xff0c;简称脉宽调制频率(f)&#xff1a;一秒钟PWM有多少个周期(单位Hz)周期(T)&#xff1a;一个周期的时间占空比(duty)&#xff1a;在一个脉冲周期内&#xff0c;高电平的时间与整个周期时间的比例脉宽时间&#xff1a;一个…

React 中的 ref 如何操作 dom节点,使输入框获取焦点

聚焦文字输入框 .focus() 获取焦点 当用户点击按钮时&#xff0c;handleClick 函数会被调用&#xff0c;从而将焦点聚焦到文本输入框上。 // 焦文字输入框 import { useRef } from "react";const FocusForm () > {const inputRef useRef<any>(null);func…

C到C++的升级

C和C的关系 C继承了所有C语言的特性&#xff1b;C在C的基础上提供了更多的语法和特性&#xff0c;C语言去除了一些C语言的不好的特性。C的设计目标是运行效率与开发效率的统一。 变化一&#xff1a;所有变量都可以在使用时定义 C中更强调语言的实用性&#xff0c;所有的变量…

内网隧道代理技术(二十二)之 CS针对特定端口上线不出网机器

CS工具自带上线不出网机器 如图A区域存在一台中转机器,这台机器可以出网,这种是最常见的情况。我们在渗透测试的过程中经常是拿下一台边缘机器,其有多块网卡,边缘机器可以访问内网机器,内网机器都不出网。这种情况下拿这个边缘机器做中转,就可以使用CS工具自带上线不出网…

代码随想录Day_53打卡

①、最长公共子序列 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符…

Flutter状态管理 — 探索Flutter中的状态

前言 随着响应式编程的理念&Flutter被大众所了解以来&#xff0c;状态管理一直是一个引人深思的话题。如果想要学习好Flutter这样的响应式的编程框架就一定是离不开状态管理的。我遇到过很多没有了解过响应式编程框架的&#xff0c;或者从事后端开发&#xff0c;自己想用F…

jmeter While控制器

一种常见的循环控制语句&#xff0c;用于重复执行一段代码块&#xff0c;直到指定的条件不再满足。 参数&#xff1a; 空LASTJMeter变量、函数、属性或任意其他可用表达式 &#xff08;jmeter提供的方法&#xff09;。判断变量值count_num小于等于20&#xff0c;推荐简单的几…

Python入门教程 | Python3 元组(tuple)

创建元组 Python 的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。 元组使用小括号 ( )&#xff0c;列表使用方括号 [ ]。 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可。 >>> tup1 (Google, Tarzan, 1997, …

组相联cache如何快速实现cache line eviction并使用PMU events验证

如何快速实现cache line eviction 一&#xff0c;什么是cache hit、miss、linefill、evict &#xff1f;1.1 如果要程序员分别制造出cache hit、miss、linefill、evict这四种场景&#xff0c;该怎么做&#xff1f; 二&#xff0c;实现cache line eviction的方法1.1 直接填充法3…

STM32WB55开发(1)----监测STM32WB连接状态

STM32WB55开发----1.监测STM32WB连接状态 概述硬件准备视频教学样品申请选择芯片型号配置时钟源配置时钟树RTC时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙LED配置设置工程信息工程文件设置参考文档SVCCTL_App_Notification结果演示 概述 STM32WB系列…

useRef 定义的 ref 在控制台可以打印但是页面不生效?

useRef 是一个 React Hook&#xff0c;它能让你引用一个不需要渲染的值。 点击计时器 点击按钮后在控制台可以打印但是页面不生效。 useRef 返回的值在函数组件中不会自动触发重新渲染&#xff0c;所以控制台可以显示变化而按钮上无法显示 ref.current的变化。 import { use…