【初阶数据结构和算法】leetcode刷题之设计循环队列

在这里插入图片描述

文章目录

  • 一、实现循环队列
    • 1.大致思路分析
    • 2.循环队列的结构定义和初始化
      • 结构定义
      • 初始化
    • 3.循环队列的判空和判满
      • 判空和判满难点分析
      • 判空
      • 判满
    • 4.循环队列的入队列和出队列
      • 入队列
      • 出队列
    • 5.循环队列取队头和队尾元素
      • 取队头元素
      • 取队尾元素
    • 6.循环队列的销毁
    • 7.最后题解源码

一、实现循环队列

题目链接:https://leetcode.cn/problems/design-circular-queue/description/

1.大致思路分析

   我们来看看题目描述:
在这里插入图片描述
   这是leetcode上的一道中等难度的题,一般来说,leetcode上简单的题不一定简单,中等一定很麻烦,这道题虽然核心也不难,但是就是麻烦
   在做这道题之前,我们要先来介绍一下什么是循环队列,循环队列是一种特殊的队列,它的首尾在逻辑上是相连的,但是还是队头出数据,队尾入数据,保持队列先进先出的特性,被称为环形缓冲器
   但是它和我们之前实现的队列的最大不同是,之前我们实现的队列如果容量不够是可以扩容的,是可变的,而循环队列的容量确定之后就不能再更改,比如后面我们写初始化方法时,会给定一个值给我们初始化,然后我们开辟好空间后就不再修改它的大小了,所以循环队列的容量一经确定就不会再更改
   那么我们接下来大致来分析一下循环队列的结构,我们是继续使用实现队列时的链表,还是说使用数组呢?其实两个都可以,只是使用链表需要使用循环链表,那么哪一个结构更优呢?
   循环队列和普通队列的一个较大区别是,循环队列的大小是固定好了的,当我们去删除数据时,不会直接释放空间,而是会留着存储以后新插入的数据,那么很明显数组的优势更大一些
   因为数组数据的删除可以只改变size,比如尾删就是让size- -,这样就间接实现了删除,我们访问不到原本的尾数据了,但是其实那个空间还在,只是我们size- -后访问不到了而已
   但是链表数据的删除会释放节点,如果不释放节点的话让它强行留下来就会很麻烦,但是我们循环队列的大小又是固定的,到时候重新申请节点插入到原位置又很麻烦,并且链表的开销比数组要大,所以综上我们实现循环链表最好使用数组
   由于队列要遵循头删尾入,所以我们最好像定义队列一样,定义两个下标front和rear分别指向循环队列的头和尾,方便进行操作,注意:rear和之前顺序表的size一样,不是指向有效元素中的最后一个元素,而是这个最后一个有效元素的下一个位置
   那么数组怎么实现首尾相连呢?首尾相连就是尾的下一个节点是头,实现这个需要一定的技巧性,要使用模运算,当rear或者front越界时,模上容量大小就可以让它们重新回到开头,就像首尾相连了一样,如图:
在这里插入图片描述

   现在我们大致知道了循环队列的底层存储,接下来就边做题边讲解思路

2.循环队列的结构定义和初始化

结构定义

   现在我们知道了使用数组,所以循环队列中肯定要有一个数组,但是我们不知道数组的具体大小,需要到时候根据初始化函数的参数确定,所以我们这里直接用一个指针代替,到时候动态申请空间
   由于队列要遵循头删尾入,所以我们最好像定义队列一样,定义两个下标front和rear分别指向循环队列的头和尾(这个尾相当于顺序表中的size),方便进行操作,还有就是我们要知道申请了多大空间,所以用一个整型变量capacity来存储
   那么是否要头删使用数组的效率会很低呢?这个是不需要担心的,因为循环队列的空间不需要释放,所以头删只需要让front++即可,到后面出队列再细讲,那么最后循环队列的大致结构定义如下:

typedef struct 
{int* arr;int capacity;int rear;int front;
} MyCircularQueue;

初始化

   在初始化函数中给了我们一个参数k,就是我们要申请的空间大小,我们直接通过动态申请k个整型(后面可能会需要更改,我们先实现到这里),然后将容量置为k,两个指向头和尾的下标置为0,如下:

MyCircularQueue* myCircularQueueCreate(int k) 
{MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if(pq == NULL){perror("malloc");return NULL;}//后面这里可能会进行改动,这里做一个标记pq->arr = (int*)malloc(sizeof(int) * k);if(pq->arr == NULL){perror("malloc");return NULL;}pq->rear = pq->front = 0;pq->capacity = k;return pq;
}

3.循环队列的判空和判满

判空和判满难点分析

   循环队列的难点不是后面的入队列和出队列,而是这个部分的判空和判满,因为我们会发现队列空时front和rear相等,队列满时front和rear也相等了,它们难以区分了,如图:
在这里插入图片描述
   可以看到队列空时,front和rear相等,指向头部,那么如果队列满了呢?如图:
在这里插入图片描述
   这里的rear相当于是之前顺序表中的size,指向有效数据的下一个位置,那么现在队列满了,rear指向元素5的下一个位置,同时元素5是最后一个元素,所以根据循环首尾相连的特点,它需要指向头
   那么怎么让它回到头呢?采用的方法就是让rear%capacity,这个方法可以自动帮我们去将越界的rear放回到头,但是问题就来了,此时front和rear又相等了,那么就说明front和rear相等可能是队列为空,也可能是队列满了
   怎么解决呢?我们这样做,在开辟空间时,我们不再开辟k个空间,而是开辟k+1个空间,多开辟一个空间,有什么作用呢?我们画画图就知道了:
在这里插入图片描述
在这里插入图片描述

   是不是非常巧妙呢?但是问题又来了,当空间满了之后,front和rear不相等了,我们如何判断队列是否满了呢?
   其实也不难,我们多开了一个空间,那么实际的空间就应该是capacity+1,实际rear也要+1,所以判断循环队列空间是否为满的条件就是front等于(rear+1) % (capacity+1),现在我们简单画个图来解释,后面还有其它情况到时候再说,如下:
在这里插入图片描述
   那么现在我们分析好了之后,我们还要做一件事,就是之前我们只开辟了k个空间,现在我们要把它改成k+1个空间,其它不变,如下:

    pq->arr = (int*)malloc(sizeof(int) * (k+1));

   这个做完之后我们就可以直接来实现判空和判满了

判空

   判空的条件就是循环队列的front等于rear,如下:

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

判满

   判满的条件就是循环队列 (rear + 1) % (capacity + 1) == front,如下:

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

4.循环队列的入队列和出队列

   我们先来看看题目给出入队列和出队列操作的要求,如图:
在这里插入图片描述
   题目的意思是如果我们插入或删除成功需要返回true,虽然题目没有说,但是我们应该都能想到插入和失败是不是就要返回false了,所以我们在入队列和出队列之前要记得判断能否插入和删除数据,那么接下来我们进入对队列和出队列的具体分析

入队列

   在入队列之前,我们首先要判断循环队列能否入队列,如果它满了,说明我们不能再插入数据了,就要直接返回false
   判断完之后我们就要真正来实现入队列操作了,入队列就是往数组的最后插入数据,也就是往rear的位置插入数据,插入完之后让rear++
   但是我们要注意一个点,rear++之后可能会越界,我们要让它模上数组真正大小,也就是capacity+1,让它重新走到开头,如图:
在这里插入图片描述
在这里插入图片描述
   当然,当我们插入完数据后,不要忘记返回true,如下:

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{if(myCircularQueueIsFull(obj)){return false;}obj->arr[obj->rear++] = value;obj->rear %= obj->capacity + 1;return true;
}

出队列

   在出队列之前,我们首先要判断循环队列能否出队列,如果它为空,说明我们不能再删除数据了,就要直接返回false
   判断完之后我们就要真正来实现出队列操作了,出队列就是删除数组头的数据,但是由于我们不需要释放空间,并且这是数组,所以我们可以直接让front++,间接实现头删,如图:
在这里插入图片描述
   但是同样有一个问题,那就是front++后也可能越界,比如上面的例子中,如果再入队列几次,再去出队列,front++,是不是就刚好越界了,所以front也是有可能越界的
   为了防止越界,让循环队列首尾相连,也只需要让front % (capacity + 1)即可,跟上面入队列是rear的解决方式一样,这里就不多说了,有了思路之后我们直接来写代码,如下:

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{if(myCircularQueueIsEmpty(obj)){return false;}obj->front++;obj->front %= obj->capacity + 1;return true;
}

5.循环队列取队头和队尾元素

   在实现取队头和队尾元素操作之前,我们来看看题目的要求:
在这里插入图片描述
   题目提示我们,如果循环队列为空是不是就不能取到队头和队尾元素了,所以如果我们判断出来循环队列为空,需要直接返回-1

取队头元素

   在取队头元素之前我们需要判断循环队列是否为空,为空就返回-1,不为空我们才去取队头元素,方法也很简单,就是直接返回front位置的数据即可,如下:

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

取队尾元素

   在取队尾元素之前我们需要判断循环队列是否为空,为空就返回-1,不为空我们才去取队尾元素,但是取队尾元素会麻烦一点,因为rear不是指向最后一个有效数据,而是指向有效数据的下一个位置
   那么是不是直接返回rear-1位置的数据就可以了呢?大部分情况下是这样的,但是有一种例外,就是当rear = 0时,如果直接-1的话就变成了-1了,越界了,而它的前一个位置应该是原队列中最后一个元素,如图:
在这里插入图片描述
   所以我们可以用重新定义一个prev,让它等于rear-1,如果prev < 0,我们就让它加上循环队列的真实大小(capacity+1),在上图中rear-1为-1,加上真实大小6就变成了5,成功回到了最后一个位置
   处理完prev可能小于0的情况后,就可以直接返回数组中prev位置的数据的,如下:

int myCircularQueueRear(MyCircularQueue* obj) 
{if(myCircularQueueIsEmpty(obj)){return -1;}int prev = obj->rear-1;if(prev < 0){prev += obj->capacity + 1;}return obj->arr[prev];
}

6.循环队列的销毁

   循环队列的销毁是最简单的,只要循环队列底层的数组不为空,就将它的空间释放掉,最后释放掉整个循环队列,如下:

void myCircularQueueFree(MyCircularQueue* obj) 
{if(obj->arr)free(obj->arr);free(obj);obj = NULL;
}

7.最后题解源码

typedef struct 
{int* arr;int capacity;int rear;int front;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) 
{MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));if(pq == NULL){perror("malloc");return NULL;}pq->arr = (int*)malloc(sizeof(int) * (k+1));if(pq->arr == NULL){perror("malloc");return NULL;}pq->rear = pq->front = 0;pq->capacity = k;return pq;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{return obj->front == obj->rear;
}bool myCircularQueueIsFull(MyCircularQueue* obj) 
{return (obj->rear + 1) % (obj->capacity + 1) == obj->front;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{if(myCircularQueueIsFull(obj)){return false;}obj->arr[obj->rear++] = value;obj->rear %= obj->capacity + 1;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{if(myCircularQueueIsEmpty(obj)){return false;}obj->front++;obj->front %= obj->capacity + 1;return true;
}int myCircularQueueFront(MyCircularQueue* obj) 
{if(myCircularQueueIsEmpty(obj)){return -1;}return obj->arr[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) 
{if(myCircularQueueIsEmpty(obj)){return -1;}int prev = obj->rear-1;if(prev < 0){prev += obj->capacity + 1;}return obj->arr[prev];
}void myCircularQueueFree(MyCircularQueue* obj) 
{if(obj->arr)free(obj->arr);free(obj);obj = NULL;
}

   那么今天的刷题就分享到这里,整个栈和队列的题就暂时分享到这里,有什么疑问欢迎提出,后面我们就进入二叉树的学习啦,敬请期待吧!
   bye~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【网络通信】数据集合集!

本文将为您介绍经典、热门的数据集&#xff0c;希望对您在选择适合的数据集时有所帮助。 1 RITA 更新时间&#xff1a;2024-11-22 访问地址: GitHub 描述&#xff1a; RITA 是一个用于网络流量分析的开源框架。 该框架以 TSV 或 JSON 格式提取 Zeek 日志&#xff0c;目前支…

.net core MVC入门(一)

文章目录 项目地址一、环境配置1.1 安装EF core需要包1.2 配置数据库连接二、使用EF创建表2.1 整体流程梳理2.1 建表详细流程三、添加第一个视图3.1整体流程梳理3.1 添加视图,并显示在web里四、使用EF增加Catogory数据,并且读取数据到页面4.1整体流程梳理4.2 实现五、增加Cat…

蓝桥杯不知道叫什么题目

小蓝有一个整数&#xff0c;初始值为1&#xff0c;他可以花费一些代价对这个整数进行变换。 小蓝可以花贵1的代价将教数增加1。 小蓝可以花费3的代价将整数增加一个值,这个值是整数的数位中最大的那个(1到9) .小蓝可以花费10的代价将整数变为原来的2倍, 例如&#xff0c;如果整…

css效果

css炫彩流光圆环效果 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style>*{margin: 0;padding: 0;}body{width: 100%;height: 100vh;}.container{position: relative;width: 100%;height: 100vh…

提供html2canvas+jsPDF将HTML页面以A4纸方式导出为PDF后,内容分页时存在截断的解决思路

前言 最近公司有个系统要做一个质量报告导出为PDF的需求&#xff0c;这个报表的内容是固定格式&#xff0c;但是不固定内容多少的&#xff0c;网上找了很多资料&#xff0c;没有很好的解决我的问题&#xff0c;pdfmakde、还有html2CanvasjsPDF以及Puppeteer无头浏览器的方案都不…

【C++动态规划 子集状态压缩】2002. 两个回文子序列长度的最大乘积|1869

本文涉及知识点 C动态规划 位运算、状态压缩、枚举子集汇总 LeetCode2002. 两个回文子序列长度的最大乘积 给你一个字符串 s &#xff0c;请你找到 s 中两个 不相交回文子序列 &#xff0c;使得它们长度的 乘积最大 。两个子序列在原字符串中如果没有任何相同下标的字符&…

鸿蒙NEXT开发案例:字数统计

【引言】 本文将通过一个具体的案例——“字数统计”组件&#xff0c;来探讨如何在鸿蒙NEXT框架下实现这一功能。此组件不仅能够统计用户输入文本中的汉字、中文标点、数字、以及英文字符的数量&#xff0c;还具有良好的用户界面设计&#xff0c;使用户能够直观地了解输入文本…

[极客大挑战 2019]BabySQL--详细解析

信息搜集 进入界面&#xff1a; 输入用户名为admin&#xff0c;密码随便输一个&#xff1a; 发现是GET传参&#xff0c;有username和password两个传参点。 我们测试一下password点位能不能注入&#xff1a; 单引号闭合报错&#xff0c;根据报错信息&#xff0c;我们可以判断…

C++《二叉搜索树》

在初阶数据结构中我学习了树基础的概念以及了解了顺序结构的二叉树——堆和链式结构二叉树该如何实现&#xff0c;那么接下来我们将进一步的学习二叉树&#xff0c;在此会先后学习到二叉搜索树、AVL树、红黑树&#xff1b;通过这些的学习将让我们更易于理解后面set、map、哈希等…

Apollo9.0源码部署(Nvidia显卡)

本文参照Apollo官方部署例程&#xff0c;进行修改。解决在部署期间遇到的网络不通、编译卡死、编译失败等问题。&#xff08;安装具有时效性&#xff0c;仅供参考&#xff09; 步骤1. 安装docker,显卡驱动、nvidia插件&#xff0c;此步骤可见专栏第一、二 节 步骤2. 拉取代…

第02章_MySQL环境搭建(基础)

1. MySQL 的卸载 1.1 步骤1&#xff1a;停止 MySQL 服务 在卸载之前&#xff0c;先停止 MySQL8.0 的服务。按键盘上的 “Ctrl Alt Delete” 组合键&#xff0c;打开“任务管理器”对话 框&#xff0c;可以在“服务”列表找到“MySQL8.0” 的服务&#xff0c;如果现在“正在…

【华为】配置VXLAN构建虚拟网络实现相同网段互通(静态方式)

微思网络 厦门微思网络 组网需求 企业已经建成比较成熟的园区网络&#xff0c;但是没有专用的数据中心网络&#xff0c;所有的服务器分布在不同的部门&#xff0c;并且不具备集中放置的条件。现在用户希望在已有园区网络上构建一个虚拟网络&#xff0c;需求如下&#xff1a; 将…

linux系统运维面试题(二)(Linux System Operations Interview Questions II)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

互联网直播/点播EasyDSS视频推拉流平台视频点播有哪些技术特点?

在数字化时代&#xff0c;视频点播应用已经成为我们生活中不可或缺的一部分。监控技术与视频点播的结合正悄然改变着我们获取和享受媒体内容的方式。这一变革不仅体现在技术层面的进步&#xff0c;更深刻地影响了我们。 EasyDSS视频直播点播平台是一款高性能流媒体服务软件。E…

最新SQL Server 2022保姆级安装教程【附安装包】

目录 一、安装包下载&#xff1a; 下载链接&#xff1a;https://pan.quark.cn/s/b1c0c63d61ec 二、安装SQL Server 1.下载安装包后解压出来&#xff0c;双击打开 2.等待加载安装程序 3.点击基本安装 4.点击接受 5.点击浏览 6.在D盘新建文件夹 7.命名为【Sql Server】&…

香港大带宽服务器:助力高效网络应用

随着全球化的加速和互联网流量的持续增长&#xff0c;大带宽服务器逐渐成为企业在全球范围内运营的关键设施。香港凭借其优越的地理位置、先进的网络基础设施和政策环境&#xff0c;成为部署大带宽服务器的重要节点之一。本文将全面探讨香港大带宽服务器的核心优势、应用场景及…

设计模式:责任链实现数据流风格的数据处理

数据流风格 数据流风格是软件架构中的一种风格&#xff0c;主要是面向数据&#xff0c;用于进行流式的数据处理&#xff1b;数据流风格的代表有管道-过滤器风格和批处理序列风格&#xff0c;这里主要是指管道-过滤器风格。 管道-过滤器风格就像其名字一样&#xff0c;是以一个…

PGSQL物化视图(Materialized View)

在 PostgreSQL 中&#xff0c;物化视图&#xff08;Materialized View&#xff09;是一种特殊的数据库对象&#xff0c;它存储了查询的结果集&#xff0c;并可以定期刷新以反映基础表中的数据变化。物化视图可以提高查询性能&#xff0c;因为它减少了每次查询时重新计算数据的需…

浏览器缓存与协商缓存

1. 强缓存&#xff08;Strong Cache&#xff09; 定义 强缓存是指在缓存的资源有效期内&#xff0c;浏览器会直接使用缓存中的数据&#xff0c;而不会发起网络请求。也就是说&#xff0c;浏览器会直接从本地缓存读取资源&#xff0c;不会与服务器进行任何交互。 如何控制强缓…

JS听到了替罪的回响

这篇还是继续写JS 这是有关函数的一些内容 函数 为什么需要函数 函数是被设计为执行指定任务的代码块 函数可以把具有相同或者相似逻辑的代码包裹起来&#xff0c;通过函数调用执行这些被包裹的代码逻辑&#xff0c;这样的优势是有利于精简代码方便复用 函数使用 这是函…