数据结构刷题训练:设计循环队列(力扣OJ)

news2024/11/24 0:20:44

目录

文章目录

前言

1. 题目:设计循环队列

2. 思路

3. 分析

 3.1 定义循环队列

 3.2 创建队列

 3.3 判空和判满

 3.4 入队

 3.5 出队

 3.6 取队头队尾数据

 3.7 销毁队列

 4. 题解

总结


前言

        当谈到队列数据结构时,很多人可能会想到普通的队列,即先进先出(FIFO)的数据结构。然而,有时候我们需要一种更高效的队列实现方式,这就是循环队列。


1. 题目:设计循环队列

 题目描述:

 题目链接:

设计循环队列icon-default.png?t=N6B9https://leetcode.cn/problems/design-circular-queue/

2. 思路

        先来理解一下题意,首先队列的长度为定长k,其次就是队列可以循环利用空间。这里我们要思考一下是使用链表实现,还是使用数组实现。在设计这个环形队列的时候,我们肯定是要设计头和尾的入下图:

         当前状态视为队列满,这时如果出队元素就会空出空间此时我们可以继续入队数据如下图:

         后边满了以后再插入数据它可以绕回来,这就是循环队列。从front开始道rear结束就是队列的数据。或许有人会有疑惑:为什么不把尾指向最后一个元素,而是指向最后一个元素的后一个位置?

        这样是为了便于判断队列是否为空,我们举个例子,队列为空时front和rear是都指向开头的,如果插入一个数据,rear就要向后移动,不移动就无法判断队列是否为空。

接下来我们回到开始的问题,使用数组还是链表实现?

         我们依据上一个问题,知道rear指向的是队尾的后一个元素,那依据单链表的特性取队尾不好取。如果要好取尾就要用双向循环链表会好搞一些,当然还有其他的解决办法例如:多加一个变量size记录队列数据个数,rear指向队尾,这样也可以解决,但这样写代码会很容易让人误导,所以这里不推荐这样的方法。链表实现也是可以的,但相对数组的代码量就比较多了。使用数组实现也会简单方便的多。

 数组:

         当rear+1==front时就是为满,由于它是一个循环队列,rear在末尾后再+1就会回到头,这里可以写成这样:front==(rear+1)%(k+1)(取值范围0~k),它要始终保持front与rear之间留有一个空。这里我们就使用数组来实现。

3. 分析

        依据上述的思路,我们使用数组来实现循环队列,通过上述的思路我们可以发现这个规律,出队就front向后走,入队就rear向后走。

 3.1 定义循环队列

typedef struct {
    int* a;
    int front;
    int rear;
    int k;
} MyCircularQueue;

        a用于存放数据,和顺序表类似,使用malloc开辟空间。front为队头,rear为队尾。

 3.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;
}

         这里和之前一样,我们仅仅是定义了队列的类型,并没有创建结构体变量,为了便于后续传参,这里我们使用malloc创建一个结构体变量。有了obj就可以通过它来找到结构体中的各个成员。注意我们还需要对数组a进行开辟空间。

 3.3 判空和判满

         这里我们先来实现判满和判空操作,上述思路中我们多开辟一个空间,用于判断满和空的情况,如果rear等于front就为空,如果rear+1等于front就为满。接下来我们来实现一下:

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return (obj->front==obj->rear);
}

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

         但这样写对吗?这样写是不对的,前边思路中提到:front==(rear+1)%(k+1)为了达到返回开头的目的,所以这里需要改写(也是为了防止rear+1越界)。

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return (obj->front==obj->rear);
}

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

        这里可能就有人疑惑了,为什么rear需要模k+1返回,front就不需要呢?front也会走到尾啊。

这个当然是需要考虑的,这个操作是在出队操作时考虑的,在判满时可以不考虑,满的情况分为两种:

第一种:

 第二种:

 3.4 入队

 

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;
}

         入队操作非常简单,在入队之前先判断队是否满,如果满直接返回false(错误)。不为满就将rear位置入队数据,然后rear向后移动,这里为了便于rear返回,这里rear需要%=(k+1)。

过程如下:

 

 3.5 出队

 

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return false;

    obj->front++;
    obj->front %=(obj->k+1);
    return true;
}

         出队前要先判断队列是否为空,不为空就front++即可不需要任何修改(front开始到rear直接的为有效数据),为了便于front返回,这里可以使用取模,或者使用if语句判断,都可以。

 

 再次入队数据就会将原数据自动覆盖。

 3.6 取队头队尾数据

         取队头数据非常简单

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[obj->front];
}

         如果为空就直接返回-1,如果不为空就返回数组front下标的数据。重点在于队尾数据。

         一般情况队尾数据只需要rear-1就可以了,但是这里需要考虑rear为0的情况。如果rear为0,rear-1就越界非法访问了,我们需要的是rear返回到数组最后的位置。这里我们可以这样操作,既然我们是取模让rear达到返回到0,那我们在对rear取模之前先对rear+(k+1),这样rear取模后仍然可以取到下标为0的位置,-1就回到了最后的位置(减一后:(rear+k)%(k+1)结果为k)

 (rear+(k+1)-1)%k+1化简一下就是:(rear+k)%(k+1),代码实现:

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

 3.7 销毁队列

        销毁队列也非常简单,先销毁啊数组,再销毁obj。

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

 4. 题解

 整体代码:

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->front==(obj->rear+1)%(obj->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;
}

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;
    else
        return obj->a[obj->front];
}

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/871404.html

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

相关文章

拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还是非常有用的

Linux之awk判断和循环

echo zhaoy 70 72 74 76 74 72 >> score.txt echo wangl 70 81 84 82 90 88 >> score.txt echo qiane 60 62 64 66 65 62 >> score.txt echo sunw 80 83 84 85 84 85 >> score.txt echo lixi 96 80 90 95 89 87 >> score.txt把下边的内容写入到s…

FL Studio for Windows-21.1.0.3713中文直装版功能介绍及系统配置要求

FL Studio 21简称FL水果软件,全称是:Fruity Loops Studio编曲,由于其Logo长的比较像一款水果因此,在大家更多的是喜欢称他为水果萝卜,FL studio21是目前最新的版本,这是一款可以让你的计算机就像是一个全功能的录音室&…

SpringBoot复习(39)Servlet容器的自动配置原理

Servlet容器自动配置类为ServletWebServerFactoryAutoConfiguration 可以看到通过Import注解导入了三个配置类: 通过这个这三个配置类可以看出,它们都使用了ConditionalOnClass注解,当类路径存在tomcat相关的类时,会配置一个T…

matlab解常微分方程常用数值解法2:龙格库塔方法

总结和记录一下matlab求解常微分方程常用的数值解法,本文将介绍龙格库塔方法(Runge-Kutta Method)。 龙格库塔迭代的基本思想是: x k 1 x k a k 1 b k 2 x_{k1}x_{k}a k_{1}b k_{2} xk1​xk​ak1​bk2​ k 1 h f ( x k , t …

ZDH-wemock模块

本次介绍基于版本v5.1.1 目录 项目源码 预览地址 安装包下载地址 wemock模块 wemock模块前端 配置首页 配置mock wemock服务 下载地址 打包 运行 效果展示 项目源码 zdh_web: https://github.com/zhaoyachao/zdh_web zdh_mock: https://github.com/zhaoyachao/z…

带你了解接收参数@PathVariable、@ModelAttribute 等相关注解

😀前言 本篇博文是关于SpringBoot 接收客户端提交数据/参数会使用到的相关注解应用说明,希望能够帮助到您😊 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文…

elasticsearch 基础

ES 搜索技术历史 今天看的是《Elasticsearch实战与原理解析》 第一章 搜索技术发展史 1、搜索技术发展史 宏观而言,搜索引擎的发展经历了五个尖端和两大分类。五个阶段分别是ftp文件检索阶段、分类目录阶段、文本相关性检索阶段、网页链接分析阶段和用户意图识别…

警惕 C++ 中的隐式类型转换

今天文章的主题灵感来自客户的一个问题: 我在研究一个代码中的栈溢出问题。为了减小栈帧的大小,我尽可能多地删除了局部变量,但仍有很多栈空间无法解释。除了局部变量、参数、保存的寄存器和返回地址之外,栈上还有什么其他的东西…

第八章 CUDA内存应用与性能优化篇(上篇)

cuda教程目录 第一章 指针篇 第二章 CUDA原理篇 第三章 CUDA编译器环境配置篇 第四章 kernel函数基础篇 第五章 kernel索引(index)篇 第六章 kenel矩阵计算实战篇 第七章 kenel实战强化篇 第八章 CUDA内存应用与性能优化篇 第九章 CUDA原子(atomic)实战篇 第十章 CUDA流(strea…

机器学习线性代数基础

本文是斯坦福大学CS 229机器学习课程的基础材料,原始文件下载 原文作者:Zico Kolter,修改:Chuong Do, Tengyu Ma 翻译:黄海广 备注:请关注github的更新,线性代数和概率论已经更新完毕…

ACL 2023 | 使用语言模型解决数学推理问题的协同推理框架

©PaperWeekly 原创 作者 | 朱欣宇 单位 | 清华大学 研究方向 | 自然语言处理 论文标题: Solving Math Word Problems via Cooperative Reasoning induced Language Models 论文链接: https://arxiv.org/abs/2210.16257 代码链接: https…

2023 互联网大厂薪资大比拼

最近整理了33家互联网大厂的薪资情况。可以看出来,大部分互联网大厂薪资还是很不错的,腾讯、阿里、美团、百度等大厂平均月薪超过30k,其他互联网大厂平均月薪也都在25k以上。01020304050607080910111213141516171819202122232425262728293031…

ESP8266(RTOS SDK)内嵌网页以实现WEB配网以及数据交互

【本文发布于https://blog.csdn.net/Stack_/article/details/131997098,未经允许不得转载,转载须注明出处】 1、执行make menuconfig,将http头由512改为更大的值,否则用电脑浏览器访问正常,但用手机浏览器访问会因为ht…

关于`IRIS/Caché`进程内存溢出解决方案

文章目录 关于IRIS/Cach进程内存溢出解决方案 描述原因相关系统变量$ZSTORAGE$STORAGE 什么情况下进程内存会变化?内存不足原理解决方案 关于 IRIS/Cach进程内存溢出解决方案 描述 在IRIS/Cach中,进程内存溢出错误是指一个进程(例如运行中的…

群晖 nas 自建 ntfy 通知服务(梦寐以求)

目录 一、什么是 ntfy ? 二、在群晖nas上部署ntfy 1. 在Docker中安装ntfy 2. 设置ntfy工作文件夹 3. 启动部署在 docker 中的 ntfy(binwiederhier/ntfy) 三、启动配置好后,如何使用ntfy 1. 添加订阅主题( Subscribe to topic…

六种不同的CRM系统类型分别有哪些特点?

企业想要管理销售,可以选择CRM系统;企业想要优化业务流程,可以选择CRM系统;企业想要提高收入,可以选择CRM系统。下面来说说,CRM是什么?六种常见CRM系统类型对比。 什么是CRM? CRM是…

当执行汇编指令MOV [0001H] 01H时,CPU都做了什么?

今天和几位单位大佬聊天时,讨论到一个非常有趣的问题-当程序执行MOV [0001H], 01H计算机实际上都做了哪些工作?乍一看这个问题平平无奇,CPU只是把立即数01H放在了地址为0001的内存里,但仔细想想这个问题远没有那么简单&#xff0c…

Synchronized八锁

/** * Description: 8 锁 * 1 标准访问,先打印短信还是邮件 ------sendSMS ------sendEmail 2 停 4 秒在短信方法内,先打印短信还是邮件 ------sendSMS ------sendEmail 3 新增普通的 hello 方法,是先打短信还是 hello ------getHello ------…