Leetcode:622. 设计循环队列 题解【具详细】

2023-12-01 02:01

本文主要是介绍Leetcode:622. 设计循环队列 题解【具详细】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、题目:

二、思路详解:

1.循环队列的存储定义

2.循环队列的创建

3.循环队列的判空与判断情况

(1) 循环队列的判空:

 (2) 循环队列的判满

4.循环队列元素的插入

5.循环队列元素的删除

6.获取队头元素

7.获取队尾元素 

8.循环队列释放

三、完整代码展示:


一、题目:

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

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

难度:中等

题目链接:622. 设计循环队列

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1);  // 返回 true
circularQueue.enQueue(2);  // 返回 true
circularQueue.enQueue(3);  // 返回 true
circularQueue.enQueue(4);  // 返回 false,队列已满
circularQueue.Rear();  // 返回 3
circularQueue.isFull();  // 返回 true
circularQueue.deQueue();  // 返回 true
circularQueue.enQueue(4);  // 返回 true
circularQueue.Rear();  // 返回 4

题目解析:就是根据题中给的接口进行函数的实现。要求我们实现一个循环队列。

用心阅读下方会有很大的收获。 

二、思路详解:

1.循环队列的存储定义

首先我们需要定义出一个循环队列的存储定义,这里采用的是使用动态数组来进行模拟循环队列,根据题中给出的接口返回类型,我们可以知道循环队列的数据类型为int 。

其次,还需定义两个记录数组的下标,一个记录队列的队头,另一个记录队列的队尾(也就是指向要入队的下一个元素的位置)。另外还要提供一个表示要存储数据的具体个数。

如图:

代码:

//采用动态数组的形式来模拟循环队列
typedef struct {int* a;     //指向数组int front;  //队头int tail;   //队尾int k;      //数据个数
} MyCircularQueue;

2.循环队列的创建

循环队列的创建,先使用malloc进行创建一个 循环队列空间

接着根据给的数据个数k让指针a指向一个动态数组,在分别对front,tail,k进行初始化,注意tail = 0表示要存放的下一个数据元素的位置,对动态数组a开辟空间的时候要多开辟一个空间,避免假溢出的现象。最后一定要返回之前创建的循环队列。

代码:

//创建长度为k的循环队列
MyCircularQueue* myCircularQueueCreate(int k) {//使用动态内存函数来申请内存//这里多申请一个空间的目的是防止假溢出//使用malloc创建一个循环队列MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//为循环队列里面的指针a ,让a指向一个长度为k+1的数组obj->a= (int*)malloc(sizeof(int)*(k+1));obj->front = 0; //队头从数组的下标0开始obj->tail = 0; //队尾指向下一个元素obj->k = k; //队列的长度为kreturn obj;
}

物理存储情况,如图:

但是我们一般会到其循环的逻辑结构,逻辑存储,如图:

 3.循环队列的判空与判断情况

循环队列的插入和删除是不可避免的,当这之前就需要先完成判和判满的接口。注意:一定要把判空和判满的函数实现放在队列插入和删除函数实现的前面。

(1) 循环队列的判空:

根据函数的返回类型是bool,空我们就返回true ,否则返回false。

  • isEmpty(): 检查循环队列是否为空。

因为这里采取的是通过动态数组来模拟循环队列,所以队列空的条件就是当front == tail 的时候,此时的循环队列就是空的。

如图:

代码:

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {//队空,就是队头与队尾相同时return obj->front == obj->tail;
}
 (2) 循环队列的判满

 同样根据函数的返回类型是bool,空我们就返回true ,否则返回false。

  • isFull(): 检查循环队列是否已满。

什么时候会满呢?当(tail+1)%(k+1) == front,就是队尾下标加1模开辟空间的个数 

可能很多会对为什么要多开辟一个空间,原因就在这:对于队列的判满的情况,

当没有创建的额外空间,队列只有数据10 和 11 的情况下,

像上图就是假溢出现象,这个队列并没有满。

总的来说:

循环队列为了区分队列的空和满,需要额外增加一个空的元素来占据队列的一个位置,这样队列满的状态就可以通过头尾指针相邻且不重合来判断,而不会出现头尾指针重合但队列实际上并不满的情况。同时循环队列需要对头尾指针进行模运算,如果没有额外的空间,那么当队列最后一个元素占据了数组最后一个位置时,下一个元素就会从数组的第一个位置开始,这样就无法正确进行模运算,而增加一个空的元素可以解决这个问题。

 代码:

bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->tail+1)%(obj->k+1) == obj->front;
}

 4.循环队列元素的插入

  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

插入前判断是否满,满就返回false,接着就是数据的插入,插入后,对tail下标进行取模(因为是反复利用原来的空间,还有就是避免溢出),插入成功就返回true。

 代码:

//插入元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//插入元素前先进行判断是否满if(myCircularQueueIsFull(obj)){return false;}//插入元素使用尾插obj->a[obj->tail] = value;obj->tail++;//避免tail的下标越界obj->tail%=(obj->k+1);return true;
}

5.循环队列元素的删除

 删除前,要进行判断是否为空。队头减一,进行删除,删除后取模。

  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

 代码:

//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//删除元素队列不能为空if(myCircularQueueIsEmpty(obj)){return false;}//出队,头删obj->front++;obj->front%=(obj->k+1);return true;
}

 6.获取队头元素

获取前进行判断,是否为空。

Front: 从队首获取元素。如果队列为空,返回 -1  

  代码:

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

7.获取队尾元素 

获取前进行判断,是否为空。

Rear: 获取队尾元素。如果队列为空,返回 -1  

  代码:

//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {//队列不能为空if(myCircularQueueIsEmpty(obj)){return -1; //队空返回-1}//注意当tail = 0的情况return obj->a[(obj->tail - 1+ obj->k+1)%(obj->k+1)];
}

解释一下 上述最后一行代码:

重点:

首先tail是指向要存放下一个元素的位置,找队尾元素时,tail要进行-1。

 因为数组下标最小是从0开始的,当tail ==0且队列不为空的情况下,上方代码obj->tail-1,就会造成0-1 == -1的情况。上方采用(obj->tail - 1+ obj->k+1)%(obj->k+1)就可以完美的避免,当然

其实可以写成

int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;// return obj->a[(obj->tail-1 + obj->k+1)%(obj->k+1)];if(obj->tail == 0){return obj->a[obj->k];}else{return obj->a[obj->tail-1];}
}

8.循环队列释放

 因为用malloc开辟的动态内存空间,为了避免内存泄漏,我们还要释放内存。注意释放的顺序。

 代码:

void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}

三、完整代码展示:

 代码: 


//采用动态数组的形式来模拟循环队列
typedef struct {int* a;     //指向数组int front;  //队头int tail;   //队尾int k;      //数据个数
} MyCircularQueue;//创建长度为k的循环队列
MyCircularQueue* myCircularQueueCreate(int k) {//使用动态内存函数来申请内存//这里多申请一个空间的目的是防止假溢出//使用malloc创建一个循环队列MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//为循环队列里面的指针a ,让a指向一个长度为k+1的数组obj->a= (int*)malloc(sizeof(int)*(k+1));obj->front = 0; //队头从数组的下标0开始obj->tail = 0; //队尾指向下一个元素obj->k = k; //队列的长度为kreturn obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {//队空,就是队头与队尾相同时return obj->front == obj->tail;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->tail+1)%(obj->k+1) == obj->front;
}//插入元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//插入元素前先进行判断是否满if(myCircularQueueIsFull(obj)){return false;}//插入元素使用尾插obj->a[obj->tail] = value;obj->tail++;//避免tail的下标越界obj->tail%=(obj->k+1);return true;
}//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//删除元素队列不能为空if(myCircularQueueIsEmpty(obj)){return false;}//出队,头删obj->front++;obj->front%=(obj->k+1);return true;
}//获取队首元素
int myCircularQueueFront(MyCircularQueue* obj) {//队列不能为空if(myCircularQueueIsEmpty(obj)){return -1; //队空返回-1}return obj->a[obj->front];
}//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {//队列不能为空if(myCircularQueueIsEmpty(obj)){return -1; //队空返回-1}//注意当tail = 0的情况return obj->a[(obj->tail - 1+ obj->k+1)%(obj->k+1)];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}/*** Your MyCircularQueue struct will be instantiated and called as such:* MyCircularQueue* obj = myCircularQueueCreate(k);* bool param_1 = myCircularQueueEnQueue(obj, value);* bool param_2 = myCircularQueueDeQueue(obj);* int param_3 = myCircularQueueFront(obj);* int param_4 = myCircularQueueRear(obj);* bool param_5 = myCircularQueueIsEmpty(obj);* bool param_6 = myCircularQueueIsFull(obj);* myCircularQueueFree(obj);
*/

这篇关于Leetcode:622. 设计循环队列 题解【具详细】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/439392

相关文章

Spring 依赖注入与循环依赖总结

《Spring依赖注入与循环依赖总结》这篇文章给大家介绍Spring依赖注入与循环依赖总结篇,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Spring 三级缓存解决循环依赖1. 创建UserService原始对象2. 将原始对象包装成工

RabbitMQ 延时队列插件安装与使用示例详解(基于 Delayed Message Plugin)

《RabbitMQ延时队列插件安装与使用示例详解(基于DelayedMessagePlugin)》本文详解RabbitMQ通过安装rabbitmq_delayed_message_exchan... 目录 一、什么是 RabbitMQ 延时队列? 二、安装前准备✅ RabbitMQ 环境要求 三、安装延时队

Python与MySQL实现数据库实时同步的详细步骤

《Python与MySQL实现数据库实时同步的详细步骤》在日常开发中,数据同步是一项常见的需求,本篇文章将使用Python和MySQL来实现数据库实时同步,我们将围绕数据变更捕获、数据处理和数据写入这... 目录前言摘要概述:数据同步方案1. 基本思路2. mysql Binlog 简介实现步骤与代码示例1

基于C#实现PDF转图片的详细教程

《基于C#实现PDF转图片的详细教程》在数字化办公场景中,PDF文件的可视化处理需求日益增长,本文将围绕Spire.PDFfor.NET这一工具,详解如何通过C#将PDF转换为JPG、PNG等主流图片... 目录引言一、组件部署二、快速入门:PDF 转图片的核心 C# 代码三、分辨率设置 - 清晰度的决定因

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

Java使用正则提取字符串中的内容的详细步骤

《Java使用正则提取字符串中的内容的详细步骤》:本文主要介绍Java中使用正则表达式提取字符串内容的方法,通过Pattern和Matcher类实现,涵盖编译正则、查找匹配、分组捕获、数字与邮箱提... 目录1. 基础流程2. 关键方法说明3. 常见场景示例场景1:提取所有数字场景2:提取邮箱地址4. 高级

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

MySQL中优化CPU使用的详细指南

《MySQL中优化CPU使用的详细指南》优化MySQL的CPU使用可以显著提高数据库的性能和响应时间,本文为大家整理了一些优化CPU使用的方法,大家可以根据需要进行选择... 目录一、优化查询和索引1.1 优化查询语句1.2 创建和优化索引1.3 避免全表扫描二、调整mysql配置参数2.1 调整线程数2.

Java实现TXT文件导入功能的详细步骤

《Java实现TXT文件导入功能的详细步骤》在实际开发中,很多应用场景需要将用户上传的TXT文件进行解析,并将文件中的数据导入到数据库或其他存储系统中,本文将演示如何用Java实现一个基本的TXT文件... 目录前言1. 项目需求分析2. 示例文件格式3. 实现步骤3.1. 准备数据库(假设使用 mysql