【LeetCode刷题之路】622.设计循环队列

在这里插入图片描述

LeetCode刷题记录
  • 🌐 我的博客主页:iiiiiankor
  • 🎯 如果你觉得我的内容对你有帮助,不妨点个赞👍、留个评论✍,或者收藏⭐,让我们一起进步!
  • 📝 专栏系列:LeetCode 刷题日志
  • 🌱 文章内容来自我的学习与实践经验,如果你有任何想法或问题,欢迎随时在评论区交流讨论。让我们一起探索更多的可能!🚀

文章目录

  • 🚀LeetCode622.设计循环队列
    • 一、🌟题目描述🌟
    • 二、🎨分析🎨
      • 链表实现分析
    • 三、💥题解💥
      • 定义队列结构
      • Create(创建队列)
      • 判断是否为空
      • 判断是不是满了
      • 获取队头元素
      • 获取队尾元素
      • push接口
      • pop接口
      • 销毁
      • ✏️总代码✏️
  • 💥💥最后贴一个题 💥💥

🚀LeetCode622.设计循环队列

链接:LeetCode622.设计循环队列

一、🌟题目描述🌟

在这里插入图片描述

二、🎨分析🎨

  • 题目理解:
    这是让我们实现的是一个 大小固定的循环队列
    正常的大小固定的队列如果满了就不能插入了
    而这里所说的循环队列 是队尾和队头连在一起的
    所以我们首先想到的就是 利用链表实现循环队列

链表实现分析

如下图:
刚开始head和tail都指向一个头!
在这里插入图片描述
这种结构下

  • 插入数据只需要把数据放到tail节点,然后tail向后走
    如图:push 1 2 3
    在这里插入图片描述
  • pop数据 只需要让head走向下一个,数据清除或者不清楚无所谓,反正可以被替换,
    如图:pop pop
    在这里插入图片描述
    注意节点不需要释放!
    而如果继续插入数据:push 4 5 6 7
    在这里插入图片描述
    可以看到此时已经满了!
    但是,大家看一下我们设计的这个有没有什么缺陷呢?
    !对! 我们可以看到!队列什么时候满呢?
    head==tail的时候满
    那么什么时候为空呢?
    head==tail的时候为空!
    所以! 这里判断空和满是有问题的!

那么解决方法是什么呢?

  1. 给一个size记录队列的大小,循环队列的节点数为k,每一次push的时候size++,pop时size–当head==tail 时,如果size为k说明已经满了,如果size为0 则说明为空
  2. 给一个flag 刚开始flag为0表示队列为空,如果head==tail了 flag置为1,表示已经满了,当再pop的时候,就把flag改成0表示未满
  3. 比较官方的做法:
    我们直接开辟k+1个链表节点,其中有一个节点不存储有效数据
    判满的条件就是 tail的下一个为head

如图:push 4 5 6 7
只能插入4 5 6 到7的时候 tail->next==head 所以已经满了,无法插入!
在这里插入图片描述
通常来说 结构就是这样的:
在这里插入图片描述

但是这时候这个队列还有别的问题吗?
我们要实现 push pop 判空 判满 获取队头数据 获取队尾数据 等等…
我们可以发现,如果想要获取队尾数据 是比较麻烦的!!
因为我们的tail是下一个要push的位置而不是真正的队尾
所以 我们如果想要解决,必须找到tail的前一个

  • 方法1: 双向链表
  • 方法2:记录尾的同时还要记录尾的前一个

显然 都是麻烦事! 所以利用链表是不方便实现的

所以我们选择用数组实现

三、💥题解💥

我们利用数组实现
先简单分析一下:
在这里插入图片描述
对于一个数组,我们要实现循环队列,那么
在这里插入图片描述
因为队列是循环的,所以什么时候队列是满的呢?
这就和我们链表部分分析的一样了,有三种方法!

  1. flag标识
  2. size记录队列大小
  3. 多开辟一个空间 ✅

我们采取对数组多开辟一个空间的方法:开辟(k+1)个空间


下面是具体接口实现:
✨代码中都有详细注释哦!!✨

定义队列结构

typedef struct {
    int* a;//动态开辟数组
    int head;//队头
    int tail;//队尾
    int k;//队的大小,因为后面传参的时候不会再传 k 所以我们定义在结构体里面
} MyCircularQueue;

Create(创建队列)

我们以k=5为例

	首先创建一个队列结构
	然后开辟出队列中大小为`k+1`的数组
	然后把结构中的head和tail初始化为0

如图:
在这里插入图片描述

判断是否为空

前面我们对链表实现的分析的时候
我们已经分析过了
当`head`==`tail`的时候,为空
只是这里的`head`和`tail`变成了下标而不是节点
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //判断是不是为空 只需要判断tail是不是等于head

    return obj->head==obj->tail;
    
}

判断是不是满了

这里是稍微有点麻烦的

首先我们要知道,判断满的条件是tail的下一个等于head
但是这是数组 !not 链表
如果是环形链表,他会自动实现很自然的循环
但是链表不一样
链表会走到一个边界,所以我们需要考虑tail的下一个是谁?
我们定义一个next来标识下一个
一般的tail的下一个就是tail+1 ,如下图
在这里插入图片描述
但是存在特殊情况:如果tail已经是最后一个位置了,那么这时候其实他的下一个就是返回开头0
在这里插入图片描述

找到下一个?
1.  定义一个next=tail+1
	如果tail==k+1
	那么next就为0
	
2.  利用取模
	我们知道
	一共有k+1个空间
	所以下标的返回时 0~ k
	这时候 next=(tail+1)%(k+1)
	不管next是不是超过了数组的返回,结果都是正确的
	
	

代码:

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //判断是不是满了
    //一般是判断tail的下一个是不是 head
    //需要考虑tail的下一个是什么?
    int next=obj->tail+1;
    if(next==obj->k+1)
    {
        next=0;
    }
    //next=(obj->tail+1)%(k+1);
    if(next==obj->head)
        return true;
    else
        return false;
}

获取队头元素

队头其实就是`head`
所以只需要访问数组中的`head`位置处的元素就可以了

但是需要注意:如果队列为空,返回-1

int myCircularQueueFront(MyCircularQueue* obj) {
    //如果队列为空 返回-1
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }

    //队头就是 head
    return obj->a[obj->head];
}

获取队尾元素

在这里插入图片描述
我们可以看到tail表示的是下一个Push的位置
而如果我们想获得队尾元素
应该是获得tail的前一个元素

所以我们定义一个prev来找到tail的前一个元素

但是也会有特殊情况

在这里插入图片描述
如果是这种情况,也就是 tail已经折回去了
tail为0 prev应该为5
怎么找到prev呢?
注意 如果我们让tail+k+1 ,tail就变成了6
这时候tail-1是不是就等于prev了?
tail为0实际上就说明了tail已经走到数组最后一个位置的后一个了


细细剖析理解一下这里:
在这里插入图片描述
在这里插入图片描述
而我们再看正确的情况是不是满足呢?
在这里插入图片描述
显然 满足的!

这样 我们就有两种情况控制边界情况
1. if判断
2. 利用取模
具体看个人喜欢用那种,小编这里就用第一种啦(比较直观)
int myCircularQueueRear(MyCircularQueue* obj) {
    //首先判断是否为空
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    //如果不为空
    //找到tail的前一个
    int prev=obj->tail-1;
    if(obj->tail==0)
        prev=obj->k;
    return obj->a[prev];
}


push接口

我们开始以k=5为例,即开辟了一个大小为6的数组

在这里插入图片描述

正常情况下,我们push只需要把tail位置放入元素,然后tail++就可以了
(向后走一步)
如图 我们把一个空的队列 push 1 2 3 4
操作过程:在这里插入图片描述

在这里插入图片描述

但是会存在特殊情况
如图:在这里插入图片描述

这时候怎么push呢?tail已经走到末尾了!

  1. 直接控制:正常走的话tail+1,tail就变成了6
    所以 如果tail==k+1 那么我们让tail=0就可以了!
  2. 利用取模
    因为数组的总空间就是k+1
    所以如果我们tail等于6了,说明tail应该走到0
    所以让tail%=(k+1),也就是 6%6==0

还有一个问题就是
什么时候push失败呢?

当满的时候会push 失败!

在这里插入图片描述
push代码:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //首先判断是不是满了
    //如果满了 push失败--返回false
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }

    //如果不满 直接插入就可以了
    obj->a[obj->tail]=value;
    //然后tail需要移动!
    obj->tail++;
    //如果移动之后tail走到末尾了 tail返回到数组的最开始
    if(obj->tail==obj->k+1)
    {
        obj->tail=0;
    }

    return true;
}

pop接口

我们来看一个例子
在这里插入图片描述
如果操作为 pop pop
那么 head就需要往前走两步,从而实现删除的效果
在这里插入图片描述
但是如果继续pop
操作: pop pop
在这里插入图片描述
可以看到,此时队列已经空了!
此时继续pop就返回false(失败)

正常情况下pop只需要让head向后走,就可以了
因为前面的数据可以随意被覆盖就相当于被删了

但是也会有特殊情况,即当head走到边界
在这里插入图片描述
此时如果继续pop head++就变成了6
但是head应该返回到数组的头部0
所以 解决方法依旧有两个:

  1. 如果head==k+1
    那么 head变成0
  2. 取模:如果head++之后变成6
    那么head=head%(k+1)

代码:

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //如果 队列已经空空了 就返回false
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //如果队列不为空
    obj->head++;
    if(obj->head==obj->k+1)
    {
        obj->head=0;
    }
    return true;
}

销毁

销毁就很简单了
只需要把结构销毁就可以了

注意:销毁结构之前,需要把结构中malloc的动态数组释放
否则容易造成内存泄漏!!

void myCircularQueueFree(MyCircularQueue* obj) {
    //首先释放数组空间
    free(obj->a);
    //然后释放队列
    free(obj);
}

总结:这道题还是比较麻烦的!
很多细节:比如边界 需要我们考虑全面!
下面是总代码供参考!

✏️总代码✏️

//使用数组实现

typedef struct {
    int* a;//动态开辟数组
    int head;//队头
    int tail;//队尾
    int k;//队的大小,因为后面传参的时候不会再传k 所以我们定义在结构体里面
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->k=k;
    obj->head=obj->tail=0;

    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //判断是不是为空 只需要判断tail是不是等于head

    return obj->head==obj->tail;
    
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //判断是不是满了
    //一般是判断tail的下一个是不是 head
    //需要考虑tail的下一个是什么?
    int next=obj->tail+1;
    if(next==obj->k+1)
    {
        next=0;
    }
    //next=(obj->tail+1)%(k+1);
    if(next==obj->head)
        return true;
    else
        return false;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //首先判断是不是满了
    //如果满了 push失败--返回false
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }

    //如果不满 直接插入就可以了
    obj->a[obj->tail]=value;
    //然后tail需要移动!
    obj->tail++;
    //如果移动之后tail走到末尾了 tail返回到数组的最开始
    if(obj->tail==obj->k+1)
    {
        obj->tail=0;
    }

    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //如果 队列已经空空了 就返回false
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //如果队列不为空
    obj->head++;
    if(obj->head==obj->k+1)
    {
        obj->head=0;
    }
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    //如果队列为空 返回-1
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }

    //队头就是 head
    return obj->a[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    //首先判断是否为空
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    //如果不为空
    //找到tail的前一个
    int prev=obj->tail-1;
    if(obj->tail==0)
        prev=obj->k;
    return obj->a[prev];
}



void myCircularQueueFree(MyCircularQueue* obj) {
    //首先释放数组空间
    free(obj->a);
    //然后释放队列
    free(obj);
}

💥💥最后贴一个题 💥💥

这个题适合 LeetCode622.循环队列中的边界问题相关的
看一下你学废了吗

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。
其队内有效长度为?(假设队头不存放数据)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C ear - front) % (N + 1)
D (rear - front + N) % (N - 1)

✨感谢阅读~ ✨
❤️码字不易,给个赞吧~❤️

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于windows环境使用nvm安装多版本nodejs

目录 前言 一、卸载node 二、nvm是什么? 三、nvm安装 1.官网下载 nvm 包 2. 安装nvm-setup.exe 3. 配置路径和下载镜像 4. 检查安装是否完成 四、 使用nvm安装node 五、修改npm默认镜像源为淘宝镜像 六、环境变量配置 1. 新建目录 2. 设置环境变量 七…

Neo4j+Neovis+Vue3:前端连接数据库渲染

Neovis(github):https://github.com/neo4j-contrib/neovis.js Neovis配置文档:neovis.js (neo4j-contrib.github.io) 一、安装Neo4j 参考文章:neo4j下载安装配置步骤-CSDN博客 二、Neovis使用 1.npm引入 ?npm ins…

《宇宙机器人》提示错误弹窗“找不到d3dx9_43.dll”是什么原因?“d3dx9_43.dll缺失”怎么解决?

电脑游戏运行时常见问题解析:《宇宙机器人》提示“找不到d3dx9_43.dll”的解决之道 TGA2024落幕,年度最佳游戏——《宇宙机器人》,作为一名在软件开发领域深耕多年的从业者,我深知电脑游戏在运行过程中可能会遇到的各种挑战&…

Cesium 限制相机倾斜角(pitch)滑动范围

1.效果 2.思路 在项目开发的时候,有一个需求是限制相机倾斜角,也就是鼠标中键调整视图俯角时,不能过大,一般 pitch 角度范围在 0 至 -90之间,-90刚好为正俯视。 在网上查阅了很多资料,发现并没有一个合适的…

28. Three.js案例-创建圆角矩形并进行拉伸

28. Three.js案例-创建圆角矩形并进行拉伸 实现效果 知识点 WebGLRenderer (WebGL渲染器) WebGLRenderer 是 Three.js 中用于渲染 3D 场景的主要渲染器。 构造器 WebGLRenderer( parameters : Object ) 参数类型描述parametersObject渲染器的配置参数,可选。 …

transformer学习笔记-自注意力机制(2)

经过上一篇transformer学习笔记-自注意力机制(1)原理学习,这一篇对其中的几个关键知识点代码演示: 1、整体qkv注意力计算 先来个最简单未经变换的QKV处理: import torch Q torch.tensor([[3.0, 3.0,0.0],[0.5, 4…

基于米尔全志T527开发板的OpenCV进行手势识别方案

本文将介绍基于米尔电子MYD-LT527开发板(米尔基于全志T527开发板)的OpenCV手势识别方案测试。 摘自优秀创作者-小火苗 米尔基于全志T527开发板 一、软件环境安装 1.安装OpenCV sudo apt-get install libopencv-dev python3-opencv 2.安装pip sudo apt…

arXiv-2024 | VLM-GroNav: 基于物理对齐映射视觉语言模型的户外环境机器人导航

作者: Mohamed Elnoor, Kasun Weerakoon, Gershom Seneviratne, Ruiqi Xian, Tianrui Guan, Mohamed Khalid M Jaffar, Vignesh Rajagopal, and Dinesh Manocha单位:马里兰大学学院公园分校原文链接:VLM-GroNav: Robot Navigation Using Phys…

Typora 修改默认的高亮颜色

shift F12 参考 怎么给typora添加颜色?

基于阿里云Ubuntu22.04 64位服务器Java及MySql环境配置命令记录

基于阿里云Ubuntu22.04 64位服务器Java及MySql环境配置命令记录 Java 23 离线环境配置MySql 环境配置MySQL常用命令 Java 23 离线环境配置 下载 Ubuntu环境下 Java 23 离线包 链接: java Downloads. 在Linux环境下创建一个安装目录 mkdir -p /usr/local/java将下载好的jdk压缩…

【树莓派4B】MindSpore lite 部署demo

一个demo,mindspore lite 部署在树莓派4B ubuntu22.04中,为后续操作开个门! 环境 开发环境:wsl-ubuntu22.04分发版部署环境:树莓派4B,操作系统为ubuntu22.04mindspore lite版本:mindspore-li…

RK3576 Android14,内存大于4G时UVC应用无法申请内存

最近有个项目需要将Linux虚拟成UVC摄像头,开发过程中遇到一个奇怪的事情,通过V4l2框架接口申请内存时,相同的板子,只是内存一个4G一个8G。4G的内存可以申请成功,8G就不行。提示“内存不足” 内存更大反而内存不足&…

HarmonyOS-高级(四)

文章目录 应用开发安全应用DFX能力介绍HiLog使用指导HiAppEvent 🏡作者主页:点击! 🤖HarmonyOS专栏:点击! ⏰️创作时间:2024年12月11日11点18分 应用开发安全 应用隐私保护 隐私声明弹窗的作…

函数与结构体(入门6)

【深基7.例1】距离函数 #include <iostream> #include <iomanip> #include <cmath> using namespace std; int main() {double x1, x2, x3, y1, y2, y3;cin >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;double d1 pow(pow(…

质数的和与积

质数的和与积 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 两个质数的和是S&#xff0c;它们的积最大是多少&#xff1f; 输入 一个不大于10000的正整数S&#xff0c;为两个质数的和。 输出 一个整…

OpenCV图像处理实战:从边缘检测到透视变换,掌握七大核心函数

一、引言 图像处理是计算机视觉领域中的基础&#xff0c;而边缘检测和轮廓分析则是其核心任务之一。OpenCV作为一个强大的计算机视觉库&#xff0c;提供了众多功能强大的函数&#xff0c;帮助开发者实现高效的图像处理。在本文中&#xff0c;我们将深入探索OpenCV中的七个重要…

JavaWeb01

JavaWeb 1. BS 和 CS BS B/S结构(Browser/server&#xff0c;浏览器/服务器模式)&#xff0c;是WEB兴起后的一种网络结构模式&#xff0c;WEB浏览器是客户端最主要的应用软件。这种模式统一了客户端&#xff0c;将系统功能实现的核心部分集中到服务器上&#xff0c;简化了系统…

开发者工具的模块化与可扩展性设计

文章目录 前言模块化设计的重要性可扩展性设计的重要性设计模式与技术实现实战代码插件管理器类&#xff1a;PluginManager注册插件方法&#xff1a;register_plugin执行插件方法&#xff1a;execute_plugin 插件实现插件 1&#xff1a;代码格式化插件插件 2&#xff1a;代码行…

嵌入式现状、机遇、挑战与展望

在当今数字化浪潮中&#xff0c;嵌入式系统宛如一颗璀璨的明珠&#xff0c;熠熠生辉&#xff0c;深刻地渗透到了我们生活的方方面面&#xff0c;成为推动现代科技进步不可或缺的关键力量。从智能家居的便捷控制&#xff0c;到工业生产的精准运作&#xff0c;再到汽车的智能驾驶…

️️️ 避坑指南:如何修复国密gmssl 库填充问题并提炼优秀加密实践20241212

&#x1f6e1;️ 避坑指南&#xff1a;如何修复国密gmssl 库填充问题并提炼优秀加密实践 ✨ 引言 在当下的数据安全环境中&#xff0c;SM4作为中国国家密码算法的代表性选择&#xff0c;被广泛应用于金融、通信和政府领域。然而&#xff0c;在实际开发中&#xff0c;即便是开…