环形队列原理,全网最通俗易懂

2023-11-15

队列是什么

队列是一种很常见的数据结构,满足先进先出的方式,如果我们设定队列的最大长度,那就意味着进队列和出队列的元素的数量实则满足一种动态平衡。

如果我们把首次添加入队列的元素作为一个一维坐标的原点,那么随着队列中元素的添加,坐标原点到队尾元素的长度会无穷无尽的增大,随这之前添入的元素不断出列,对头对应的下标点也在不断增大。这样,进队列和出队列的元素的数量就对应到对头和队尾下标点的移动

因此我们评判一个队列长度是否溢出原先约定的最大长度,实则就是在评判队尾坐标点与队头坐标点之间的差值,无论是出队列还是入队列,队头和队尾的坐标都在不断增大

front指针和rear指针的引入

虽然队尾和队头的下标在不断增大,但是我们对于队列的研究只需要局限在队头与队尾之间的元素,坐标原点到队头之间的元素已经算作出列元素,并不需要研究。因此我们不妨将队列在逻辑上放入一个事先设定容量的一维数组中,只要这个数组的容量是队列中元素的个数+1就行,为什么要这么设定待会再讲。我们想要达到的目的是,无论出列还是入列,本质上是通过修改数组中元素的值,那些已经出列的元素所在的下标位需要放置新入列的元素,并在逻辑上保证新入列元素位于队尾就行。

因此,我们不得不得引入头指针front和尾指针rear,对指针指向的数组下标对应空间进行操作,来修改数组中元素的值。

front指针和rear指针的理解

front:初始值为0,对应索引位待出列,若当前指向的数组下标元素要出列,则先执行出列动作(实际上不用操作,出列的索引位可以被新入队的元素覆盖),随后front指针就要向后一位,即front++

rear:初始值为0,对应索引位待入列,若当前指向的数组下标元素要入列,则先执行入列动作(索引位元素赋值),随后front指针就要向后一位,即rear++

队列最大长度匹配数组容量导致一种错误的解决方案

这就会有一个问题,随着队列中元素的不断更迭,front和rear很快就会超过数组容量,造成数组索引越界

比如上图所示,front=2,也就是说已经有两个元素出列了,因此rear=5与rear=6对应的两个元素理应可以入列,但是我们发现数组maxsize=5,不存在索引位5和6,强行对这两个下标赋值会造成索引越界异常indexOutException 。但是我们发现此时数组中索引位0和1都空着,完全可以将这两个位置利用起来,因此我们可以想办法让实际的rear值转化为等效的rear值,也就是是让rear=5转化为rear=0,同理rear6转化为rear=1。怎么做到呢?无疑是通过取余!

每次新元素入队后, 执行rear=(rear)%maxSize操作,随后执行rear++操作右移rear指针

像上图中的rear=rear%5乍一看好像没问题,但实际上这种取余方式是有问题的,出现这种取余方式的根源在于我们想让队列最大长度与数组容量保持一致,下文会详细说明这种解决方案的错误之处。

指针的往复移动:逻辑上的环形

出队和入队的方向是从右向左,而front与rear指针的移动方向却是从左到右循环往复(指向数组末尾后按照取余算法又重置为数组开头),因此我们可以把单向数组在逻辑上理解成环形数组,指针的循环往复移动理解成按照顺时针或逆时针(只要规定某一方向就好)单向移动

  环形队列小知识:

  环形队列是在实际编程极为有用的数据结构,它有如下特点。

  它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单。能很快知道队列是否满为空。能以很快速度的来存取数据。

   因为有简单高效的原因,甚至在硬件都实现了环形队列。 

   环形队列广泛用于网络数据收发,和不同程序间数据交换(比如内核与应用程序大量交换数据,从硬件接收大量数据)均使用了环形队列。

队列为空的判别

我们怎么判断队列为空呢?

如果我们按照指针从左到右的方向移动,当front指针和rear指针重合时,front指针对应的索引位之前的索引位都已经出列完毕,而rear指针对应的索引位以及之后的所有索引位还未有元素入列。

所以队列是否为空的判别:front==rear

rear=rear%maxSize解决方案的问题

  •  入队图示

下图展示了maxSize=5的数组中,front=0保持不变,元素依次入列直到满载,rear指针的移动情况:

  •  front=rear=0的歧义

 可以看到,如果我们认为队列容量与数组容量应该持平,那么当第五个元素50入列后,本来rear=4执行了rear++的操作后,rear=5,随后rear将会通过取余算法rear=rear%maxSize重置为0,这是我们解决方案的核心!

但关键点就在这里,我们发现空载时front=rear=0,满载时依然有front=rear=0!这样子我们就无法判断front=rear时,队列是空还是满,因此rear=rear%maxSize这种解决方案是不被允许的

新的解决方案:置空位的引入

  • 新的解决方案

       每次新元素入队后, 执行rear=(rear+1)%maxSize操作,该操作包含rear++操作

  • 置空位的引入

       并且我们人为规定,数组中必须留有一个索引位不得放置元素,必须置空!!!如何实现我们的人为规定呢?那就要先探索当数组满载后front和rear指针之间有啥关系

  •  入队图示

下图展示了maxSize=5的数组中,front=0保持不变,元素依次入列直到满载,rear指针的移动情况:

       人为的让最后一位置空,所以当元素40入列后,数组已经满载

       满载后数据之间的关系:

  • front=0
  • rear=(rear+1)%maxSize=(3+1)%5=4  (注: 执行完arr[rear]=40,再执行  rear=(rear+1)%maxSize)
  • (rear+1)%maxSize=(4+1)%5=0=front

       当我们认为的满载发生后,最后一位置空,发现此时rear和front之间的关系为(rear+1)%maxSize=(4+1)%5=0=front,因此这个关系可以作为满载的条件

       因为处于满载状态,我们无法再往队列添加元素,只能从队列取出元素,也就是进行出列的操作,而一旦我们执行了出列的操作,比如将索引位i=0上的元素10出列后,则front右移,即执行front=(front+1)%maxSize操作,最终front=1。

       若随后又添加元素入列,即在索引位i=4上添加元素50,随后又会执行rear=(rear+1)%maxSize操作,最终rear=0。

       rear=0≠front=1,此时就不会出现之前那种错误方案中 rear=front=0导致歧义的情况,而一旦 rear=front=0,必然表示队列为空,因此这种解决方案是行得通的

 队列为满的判别

      当我们认为的满载发生后,最后一位置空,发现此时rear和front之间的关系为(rear+1)%maxSize=(4+1)%5=0=front,因此这个关系可以作为满载的条件

队列中元素的个数

      numValid=(rear+maxSize-front)%maxSize,大家可以带入数据验证一下

     实际上:

       当rear在front之后(这里指的是数组中索引位的前后,并非逻辑上的前后),有效数据个数=rear-front=(rear+maxSize-front)%maxSize

       当rear在front之前(这里指的是数组中索引位的前后,并非逻辑上的前后),有效数据个数=(rear+maxSize-front)%maxSize

值得注意的一些细节

  • 细节1

      置空位虽然是人为引入的,但这不意味这置空位的位置是随意的,实际上,只有队列满后才会将剩下的位置作为置空位,一旦置空位出现,rear和front永远不可能指向同一个索引位,因为你会惊奇的发现置空位恰号将rear和front隔开了.

     置空位就像一把锁,一旦上锁就只能通过出队列操作解锁

继续执行获取元素操作出队列(解锁):

上图中60入列后满载,可以看到置空位再次出现,但30➡40➡50➡60➡置空位 形成了逻辑上的闭环

  • 细节2

从闭环的角度理解,front永远不可能在循环中超过rear,最多只能和rear相遇。

因为置空位的出现,rear不可能拉front一圈,也就避免了rear在超过front的情况下主动与front相遇

下图中的maxSize-1对应的就是置空位,rear是无法越过置空位的。只有front主动顺时针追赶上rear,它俩才会相遇,而此时队列内就没有元素,为空

 

  •  细节3

队列的最大长度queueMaxsize=数组容量arrayMaxSize-1  (由于置空位要占一位)

JAVA代码实现环形队列

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

环形队列原理,全网最通俗易懂 的相关文章

  • 唯一索引或主键违规:“PRIMARY KEY ON PUBLIC.xxx”; SQL语句

    每当我的应用程序启动时 我都会收到以下错误消息 Caused by org h2 jdbc JdbcSQLException Unique index or primary key violation PRIMARY KEY ON PUBL
  • 热重载在docker中运行的java程序

    我开发了一个java程序 应该在docker中运行 然而 我在调试docker中运行的java程序时遇到了很多痛苦 我在网上搜索 一些教程提出了像 spring dev tools 这样的工具 因为我的java程序是基于spring boo
  • Java 中的 XPath 节点集

    我在 eclipse 中有这段代码 NodeSet nodes NodeSet xPath evaluate expression inputSource XPathConstants NODESET 它给我 NodeSet 上的编译时错误
  • 解决错误:日志已在具有多个实例的atomikos中使用

    我仅在使用atomikos的实时服务器上遇到问题 在我的本地服务器上它工作得很好 我在服务器上面临的问题是 init 中出错 日志已在使用中 完整的异常堆栈跟踪 java lang RuntimeException Log already
  • JNI 不满意链接错误

    我想创建一个简单的 JNI 层 我使用Visual studio 2008创建了一个dll Win 32控制台应用程序项目类型 带有DLL作为选项 当我调用本机方法时 出现此异常 Exception occurred during even
  • HDFS:使用 Java / Scala API 移动多个文件

    我需要使用 Java Scala 程序移动 HDFS 中对应于给定正则表达式的多个文件 例如 我必须移动所有名称为 xml从文件夹a到文件夹b 使用 shell 命令我可以使用以下命令 bin hdfs dfs mv a xml b 我可以
  • 如何为 Gson 编写自定义 JSON 反序列化器?

    我有一个 Java 类 用户 public class User int id String name Timestamp updateDate 我收到一个包含来自 Web 服务的用户对象的 JSON 列表 id 1 name Jonas
  • Clip 在 Java 中播放 WAV 文件时出现严重延迟

    我编写了一段代码来读取 WAV 文件 大小约为 80 mb 并播放该文件 问题是声音播放效果很差 极度滞后 你能告诉我有什么问题吗 这是我的代码 我称之为doPlayJframe 构造函数内的函数 private void doPlay f
  • 在具有相同属性名称的不同数据类型上使用 ModelMapper

    我有两节课说Animal AnimalDto我想用ModelMapper将 Entity 转换为 DTO 反之亦然 但是对于具有相似名称的一些属性 这些类应该具有不同的数据类型 我该如何实现这一目标 动物 java public class
  • Spring Data 与 Spring Data JPA 与 JdbcTemplate

    我有信心Spring Data and Spring Data JPA指的是相同的 但后来我在 youtube 上观看了一个关于他正在使用JdbcTemplate在那篇教程中 所以我在那里感到困惑 我想澄清一下两者之间有什么区别Spring
  • 如何在 JFreeChart TimeSeries 图表上显示降雨指数和温度?

    目前 我的 TimeSeries 图表每 2 秒显示一个位置的温度 现在 如果我想每2秒显示一次降雨指数和温度 我该如何实现呢 这是我的代码 import testWeatherService TestWeatherTimeLapseSer
  • 将多模块 Maven 项目导入 Eclipse 时出现问题 (STS 2.5.2)

    我刚刚花了最后一个小时查看 Stackoverflow com 上的线程 尝试将 Maven 项目导入到 Spring ToolSuite 2 5 2 中 Maven 项目有多个模块 当我使用 STS 中的 Import 向导导入项目时 所
  • Tomcat 6找不到mysql驱动

    这里有一个类似的问题 但关于类路径 ClassNotFoundException com mysql jdbc Driver https stackoverflow com questions 1585811 classnotfoundex
  • 当单元格内的 JComboBox 中有 ItemEvent 时,如何获取 CellRow

    我有一个 JTable 其中有一列包含 JComboBox 我有一个附加到 JComboBox 的 ItemListener 它会根据任何更改进行操作 但是 ItemListener 没有获取更改的 ComboBox 所在行的方法 当组合框
  • 运行 Jar 文件时出现问题

    我已将 java 项目编译成 Jar 文件 但运行它时遇到问题 当我跑步时 java jar myJar jar 我收到以下错误 Could not find the main class myClass 类文件不在 jar 的根目录中 因
  • Keycloak - 自定义 SPI 未出现在列表中

    我为我的 keycloak 服务器制作了一个自定义 SPI 现在我必须在管理控制台上配置它 我将 SPI 添加为模块 并手动安装 因此我将其放在 module package name main 中 并包含 module xml 我还将其放
  • 将 JTextArea 内容写入文件

    我在 Java Swing 中有一个 JTextArea 和一个 提交 按钮 需要将textarea的内容写入一个带有换行符的文件中 我得到的输出是这样的 它被写为文件中的一个字符串 try BufferedWriter fileOut n
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour
  • JAVA - 如何从扫描仪读取文件中检测到“\n”字符

    第一次海报 我在读取文本文件的扫描仪中读取返回字符时遇到问题 正在读取的文本文件如下所示 test txt start 2 0 30 30 1 1 90 30 0 test txt end 第一行 2 表示两个点 第二行 位置索引 0 xp
  • Jackson 将单个项目反序列化到列表中

    我正在尝试使用一项服务 该服务为我提供了一个带有数组字段的实体 id 23233 items name item 1 name item 2 但是 当数组包含单个项目时 将返回该项目本身 而不是包含一个元素的数组 id 43567 item

随机推荐

  • WIN10家庭版虚拟机启动蓝屏问题

    关于在WIN10家庭版系统上安装VMware workstation每次启动虚拟机 主机就会出现蓝屏得问题 在刚安装好得时候 启动并没有出现蓝屏 但是使用了几次以后蓝屏几乎每次都会出现 网上查了一下发现好多网友也都遇到得了类似得问题 但是每
  • python绘制qq图_Python中作QQ图(quantilequantile Plot)

    Q Q图主要可以用来回答这些问题 两组数据是否来自同一分布 PS 当然也可以用KS检验 利用python中scipy stats ks 2samp函数可以获得差值KS statistic和P值从而实现判断 两组数据的尺度范围是否一致 两组数
  • HighChar 详解-双Y轴-及各

    网上的例子 数据都是写死的 有点不实用吧 我在这里举一个 展示功能需求的数据 按需从数据库获取并画图展示 本例子结合 angular js 其他前台框架同理 从后台获取数据即可 1 首先要引入Jquery JS 再引入相关highChar
  • 上升子序列用C语言编写,最长上升子序列(C语言 动态规划)

    描述 一个数的序列bi 当b1 lt b2 lt lt bS的时候 我们称这个序列是上升的 对于给定的一个序列 a1 a2 aN 我们可以得到一些上升的子序列 ai1 ai2 aiK 这里1 i1 lt i2 lt lt iK N 比如 对
  • 在html中写的css没效果,css样式不起作用是什么原因?

    在写页面时 有时会发现自己写的css样式无法生效 我们该如何排查css样式无法生效 常见的css样式不起作用的原因有哪些呢 下面我们就来看一下css样式不起作用的原因 排查css样式不起作用的方法步骤 首先 先试一下清除缓存 重启浏览器等手
  • 用 Python 制作一个艺术签名小工具,给自己设计一个优雅的签名

    生活中有很多场景都需要我们签字 签名 如果是一些不重要的场景 我们的签名好坏基本无所谓了 但如果是一些比较重要的场景 如果我们的签名比较差的话 就有可能给别人留下不太好的印象了 俗话说字如其人嘛 本文我们使用 Python 来制作一个艺术签
  • 逐行扫描型Memory LCD显存管理与emWin移植

    因为Memory LCD 的特性 不能设置像素坐标 只能用缓存整体刷新 所以对于Memory LCD来说 emWin移植仅与打点函数有关 这里用Sharp Memory LCD ls013b7dh03 作为实例 LCD的显存 逐行扫描 存放
  • 缓存记录

    1 我们在项目中使用缓存通常都是先检查缓存中是否存在 如果存在直接返回缓存内容 如果不存在就直接查询数据库然后再缓存查询结果返回 这个时候如果我们查询的某一个数据在缓存中一直不存在 就会造成每一次请求都查询DB 这样缓存就失去了意义 在流量
  • 简述锂离子电池的分类及结构

    锂离子电池按所用电解质材料的不同 可分为液态锂离子电池 LIB 和聚合物锂离子电池 PLB 两类 锂电池按工作环境分 高温锂离子电池 低温锂离子电池 常温锂离子电池 按电解质状态分 液态锂离子电池 凝胶锂离子电池固态锂离子电池 按形状分 方
  • uniapp实现横向滚动

  • 【软考】【系统架构设计师】决策论知识点

    1 概念 决策 一词来源于英语Decision Analysis 直译为 做出决定 所谓决策 就是为了实现预定的目标在若干可供选择的方案中 选出一个最佳行动方案的过程 它是一门帮助人们科学地决策的理论 也是管理者识别并解决问题及利用机会的过
  • SQLMAP脚本-sql-labs-Less-26-27a

    testtest sqli labs less 26 and less 26a 观察后端代码 发现空格 or and以及注释符 和 都没了 or and用双写 注释使用 00 空格用 09 0A 0B 0D 20 编写sqlmap脚本命名为
  • PHP运行模式

    PHP运行模式有4钟 1 cgi 通用网关接口 Common Gateway Interface 2 fast cgi 常驻 long live 型的 CGI 3 cli 命令行运行 Command Line Interface 4 web
  • centOS7 安装docker

    centOS7 安装docker centOS7 先更新yum 更新yum sudo yum update 下载必须 yum utils device mapper persistent data lvm2 sudo yum install
  • 本地Docker镜像发布到阿里云的Docker Hub

    1 配置镜像加速器 参考https my oschina net u 182501 blog 1549194 2 命名空间管理 进入https cr console aliyun com namespace index 3 创建镜像仓库 h
  • Java源文件注释

    关于java源程序当中的注释 什么是注释 注释的作用是什么 出现在java的源程序当中 对java源代码的解释说明 注释不会被编译到 class字节码文件当中 一个好的开发习惯应该是多编写注释 这样程序的可读性比较强 java中的注释怎么写
  • C++ 多态 超详细讲解

    文章目录 多态概念引入 1 C 中多态的实现 1 1 多态的构成条件 1 2 虚函数 1 3虚函数的重写 1 4 C 11 override final 1 5 重载 覆盖 重写 重定义 隐藏 2 抽象类 2 1 抽象类的概念 2 2 接口
  • MySQL第四讲 MySql Undo日志 - 对聚簇索引进行CUD操作

    事务需要保证原子性 如果在事务执行过程中出现以下情况 就需要用到undo log 1 事务执行中遇到各种错误 比如服务器本身的错误 操作系统错误甚至是断电导致的错误 2 事务在执行过程手动rollback结束当前事务 每当对一条记录进行改动
  • STM32 定时器中断

    通用定时器工作过程 时钟选择 计数器时钟可以由下列时钟源提供 内部时钟 CK INT 外部时钟模式1 外部输入脚 TIx 外部时钟模式2 外部触发输入 ETR 内部触发输入 ITRx 使用一个定时器作为另一个定时器的预分频器 如可以配置一个
  • 环形队列原理,全网最通俗易懂

    队列是什么 队列是一种很常见的数据结构 满足先进先出的方式 如果我们设定队列的最大长度 那就意味着进队列和出队列的元素的数量实则满足一种动态平衡 如果我们把首次添加入队列的元素作为一个一维坐标的原点 那么随着队列中元素的添加 坐标原点到队尾