[Data structure]队列环形队列 | 一文带你彻底搞懂队列和环形队列(内附详细图解和代码实现)

article/2025/9/28 0:54:05

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现
⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正,一同进步😁

目录

  • 1、简介
  • 2、应用场景
  • 3、优缺点
  • 4、图解
    • 4.1、普通队列
    • 4.2、环形队列
  • 5、代码实现(Java)
    • 5.1、普通队列实现
    • 5.2、环形队列实现

1、简介

队列分为两种,一种是简单队列,一种是环形队列

队列是一种常见的数据结构,它是一种线性结构,可以理解为只允许在一端进行插入操作,而在另一端进行删除操作的线性表。在队列中,进行插入操作的一端称为队尾,进行删除操作的一端称为队头。
队列的特点是先进先出,即先插入的元素先被删除。队列可以用于很多场景,比如计算机中的任务调度、打印机任务队列等等。

环形队列是一种特殊的队列,它是在队列的基础上添加了一些限制条件,使得队列可以在固定大小的存储空间下进行循环使用。环形队列可以用数组实现,数组中的元素按照一定的顺序排列,并且当队列头或者队列尾指针到达数组的尾部时,会自动从数组的头部开始重新循环使用。

环形队列的一个好处是,当队列满时,可以通过覆盖队列头部的元素来继续存储新的元素,这样可以使得队列在一定程度上具有循环使用的能力,节省存储空间。但是在使用环形队列时需要注意一些细节问题,比如队列空、队列满、队列大小等等。

2、应用场景

队列和环形队列在计算机科学和工程中有许多应用场景,以下是一些例子:
  1.任务调度:操作系统可以使用队列来实现任务调度,将各个进程放入队列中按照优先级进行调度执行。
  2.消息传递:在消息队列系统中,消息被放入队列中,并按照一定的顺序被处理。
  3.网络通信:网络传输中,消息包可以被组织成队列的形式,以确保它们以正确的顺序被传输和处理。
  4.打印机任务队列:打印机可以使用队列来管理多个打印任务,确保它们按照正确的顺序进行打印。
  5.广度优先搜索算法:广度优先搜索算法可以使用队列来实现,搜索树的每一层节点可以被放入队列中,
   以便按照广度优先的顺序进行搜索。
  6.循环播放音频:在音频播放器中,可以使用环形队列来实现循环播放,确保音频可以以连续的方式进行播放。
  7.缓存:在计算机系统中,缓存可以使用队列来实现,最近使用的数据可以放入队列的队尾,以便快速访问。
总之,队列和环形队列在很多场景中都有着广泛的应用,它们是实现许多计算机科学和工程问题的重要工具。

3、优缺点

队列和环形队列的优缺点如下:

  1. 队列的优点:
    1.1. 先进先出的特性可以确保处理顺序的正确性
    1.2. 简单易用,具有清晰的操作方式
    1.3. 适合于需要按顺序进行处理的场景。
  2. 队列的缺点:
    2.1. 无法在任意位置插入和删除元素,只能在队列的两端进行操作
    2.2. 如果队列的长度未知,可能会导致存储空间的浪费。
  3. 环形队列的优点:
    3.1. 可以循环利用存储空间,节省存储空间
    3.2. 可以快速地在队列头和队列尾进行操作
    3.3. 具有固定大小的存储空间,可以避免内存泄漏等问题。
  4. 环形队列的缺点:
    4.1. 需要额外的指针来维护队列的状态,增加了复杂度
    4.2. 不能有效地利用存储空间,因为一旦队列满了,就需要覆盖队列头的元素
    4.3. 队列的大小必须预先定义好,难以动态调整。

综上所述,队列和环形队列各自有其优缺点,需要根据具体的应用场景来选择合适的数据结构。如果需要按照顺序处理元素并且不需要动态调整队列的大小,可以选择队列;如果需要节省存储空间并且能够循环利用队列的存储空间,可以选择环形队列。

4、图解

4.1、普通队列

在这里插入图片描述

实现思路:

  1. front 就指向队列的第一个元素的前一个位置, 也就是font队头索引值为-1
  2. rear 指向队列的最后一个元素,rear初始值为-1
  3. 当队列满时,条件是 rear == maxSize - 1 【满】
  4. 对队列为空的条件, rear == front【空】
  5. 队列中有效的数据的个数 (rear - front)
    后面要在这个实现思路上面做优化,优化为环形队列

4.2、环形队列

在这里插入图片描述
实现思路:

  1. front 变量的含义做一个调整: front 就指向队列的第一个元素, 也就是说 arr[front] 就是队列的第一个元素
    front 的初始值 = 0
  2. rear 变量的含义做一个调整:rear 指向队列的最后一个元素的后一个位置. 因为希望空出一个空间做为约定.
    rear 的初始值 = 0
  3. 当队列满时,条件是 (rear + 1) % maxSize == front 【满】
  4. 对队列为空的条件, rear== front【空】
  5. 当我们这样分析, 队列中有效的数据的个数 (rear + maxSize - front) % maxSize
  6. 我们就可以在原来的队列上修改得到,一个环形队列

此处队列满,有效数据个数的分析如下:

  1. front=2,rear=0,MaxSize假设为5,那么最多存储4个数据(约定俗成,后面解释)
  2. 当rear>front,有效数据为:rear-front
  3. 当rear<front,环形队列就生效了。此时最后一个出队列的数据的索引值,排在了第一个出队列的数据的索引值之后,那么就要算出(后索引减前索引为多少,即rear-front),发现是负的。
  4. 此时的有效数据就应该为:(rear + maxSize - front) % maxSize。这里为什么要加maxSize,是因为要补偿rear-front为负数的那部分
  5. 比如rear-front=-2,-2+MaxSize=3,3%5=3,所以有效个数是三个。
    求出是-2,说明此时距离队列全部填充数据,还少了两个数据(一个是约定的空一个位置,一个是rear和front之间),那么此时加上maxSize,刚好弥补这两个欠缺的数据(因为数组长度有限,所以抵消掉负数之后,剩下的就是有效数据)

下面说一下关于为什么队列满的索引值是 (maxSize-1)-1 :
这是一个约定俗成的记法,只是为了增加代码的阅读性,此处浪费数组最后一个存储空间进行约定。也可以不这么处理。

5、代码实现(Java)

5.1、普通队列实现

先看一下代码总体实现思路:
  在这里插入图片描述

代码编写流程:

  1. 编写有参构造器,传入整数初始化数组容量,在构造器中对front和rear"指针"初始化为-1;
  2. 判断队列是否为空
  3. 判断队列是否为满
  4. 判断队列有多少个有效数据
  5. 添加数据
  6. 取出数据
  7. 显示头数据

重点是取数据、添加数据和有效数据的个数:
  在这里插入图片描述
  在这里插入图片描述
  在这里插入图片描述

完整代码如下(内附详细注释):

package queueArray;import java.util.Arrays;/*** @author 逐梦苍穹* @date 2023/4/17 13:44*/
public class QueueArray {public int maxSize; //确定最大容量public int front; //队列头public int rear; //队列尾public int[] array; //数组实现队列/*** 初始化构造器。此处有一个JavaSE的知识:声明了有参构造器,则自动缺失了无参构造器*/public QueueArray(int maxSize) {this.maxSize = maxSize;front = -1;rear = -1;array = new int[maxSize];}/*** 判断队列是否为空*/public boolean isEmpty() {return rear == front;}/*** 判断队列是否为满*/public boolean isFull() {return rear == (maxSize - 1);}/*** 判断队列中存放了多少个有效数据*/public int effectiveData() {return (rear - front);}/*** 添加数据到队列(先进后出原则)*/public void addQueue(int addData) {//先判断是否为满if (isFull()) {System.out.println("队列满,无法添加");} else {rear++;array[rear] = addData;}}/*** 从队列中取数据(先进后出原则)*/public int getQueue() {//判断是否空if (isEmpty()) {System.out.println("队列空,无法获取");return 0;} else {front++; //front一开始索引是-1,所以要先自增1才能取到队列中的数据int temporary = array[front];array[front] = 0;return temporary;}}/*** 显示当前队列中所有的有效数据*/public void showQueueData() {//判断是否为空if (isEmpty()) {System.out.println("队列空,无法显示");} else {for (int i = front + 1; i <= rear; i++) {System.out.printf("array[%d] = %d\n", i, array[i]);}}}/*** 显示头数据(并非取出)*/public void showHeadData() {if (isEmpty()) {System.out.println("队列空,无法显示头数据");} else {System.out.println(array[front + 1]);}}@Overridepublic String toString() {return "QueueArray{" +"maxSize=" + maxSize +", front=" + front +", rear=" + rear +", array=" + Arrays.toString(array) +'}';}
}

普通队列存在的问题:空间无法复用。只能单向存储,无法循环存储:
  在这里插入图片描述

5.2、环形队列实现

现环形队列的重要思想:取模(对maxSize取模)

相比于之前的普通队列实现,在普通队列代码的基础上,作出如下修改:
1. front和rear初始化为0,指向数组第一个位置
2. rear的最大值不再是maxSize-1,而是预留了一个作为约定。即为(maxSize - 1 -1)
3. 判断队列是否为满:(rear + 1) % maxSize == front

4. 有效数据的个数:(rear - front + maxSize) % maxSize
5. 添加数据的rear"指针":rear = (rear + 1) % maxSize
5. 取出数据的front"指针":front = (front + 1) % maxSize

6. 显示所有数据的for循环起止条件

修改部分的代码如下:
  在这里插入图片描述
  在这里插入图片描述
  在这里插入图片描述
  在这里插入图片描述
  在这里插入图片描述
在这里插入图片描述

代码的总体实现思路和之前的普通队列一致。

下面是环形队列的完整代码:

package queueArray;import java.util.Arrays;/*** @author 逐梦苍穹* @date 2023/4/17 13:44*/
public class CircleQueueArray {public int maxSize; //确定最大容量public int front; //队列头public int rear; //队列尾,最后一个存储数据的索引为 (maxSize - 1) - 1public int[] array; //数组实现队列/*** 初始化构造器。此处有一个JavaSE的知识:声明了有参构造器,则自动缺失了无参构造器*/public CircleQueueArray(int maxSize) {//front和rear不需要初始化,在环形队列的实现中默认为0this.maxSize = maxSize;array = new int[maxSize];}/*** 判断队列是否为空:*/public boolean isEmpty() {return rear == front;}/*** 判断队列是否为满:(rear + 1) % maxSize == front*/public boolean isFull() {//使用这种方式是因为环形队列,rear有可能是在front的后面return (rear + 1) % maxSize == front;}/*** 判断队列中存放了多少个有效数据*/public int effectiveData() {//加上maxSize是为了弥补当rear<front这种情况下空出来的数据return (rear - front + maxSize) % maxSize;}/*** 添加数据到队列(先进后出原则)*/public void addQueue(int addData) {//先判断是否为满if (isFull()) {System.out.println("队列满,无法添加");} else {//这里先把数据添加,再把rear后移array[rear] = addData;//后移要取模,因为要同时考虑普通队列和循环队列的情况rear = (rear + 1) % maxSize;}}/*** 从队列中取数据(先进后出原则)*/public int getQueue() {//判断是否空if (isEmpty()) {System.out.println("队列空,无法获取");return 0;} else {//front默认为0int temporary = array[front];array[front] = 0;//front指针下移一位,但是这里要取模front = (front + 1) % maxSize;return temporary;}}/*** 显示当前队列中所有的有效数据*/public void showQueueData() {//判断是否为空if (isEmpty()) {System.out.println("队列空,无法显示");} else {//现在是从front开始for (int i = front; i < front + effectiveData(); i++) {System.out.printf("array[%d] = %d\n", i % maxSize, array[i % maxSize]);}}}/*** 显示头数据(并非取出)*/public void showHeadData() {if (isEmpty()) {System.out.println("队列空,无法显示头数据");} else {System.out.println(array[front]);}}@Overridepublic String toString() {return "CircleQueueArray{" +"maxSize=" + maxSize +", front=" + front +", rear=" + rear +", array=" + Arrays.toString(array) +'}';}
}

到这里,队列和环形队列就介绍完了。
如果有什么错误请大家不吝赐教
也可以一起探讨学习😊


http://chatgpt.dhexx.cn/article/y4A8ooN1.shtml

相关文章

C语言环形队列

#include <stdio.h> #define Len 6 unsigned char Input_Buff[6] {0}; //用户输入缓冲区 unsigned char Input_Num 0; //输入队列数据字节数 unsigned char Output_Num 0; //从队列取出的数据字节数 struct Queue {unsigned char Buffer[Len]; unsigned c…

【数据结构】队列、环形队列

目录 1.队列的概念及结构 2.队列的实现 3.队列的相关实现函数与源代码 3.1初始化队列 3.2 队尾入队列 3.3 队头出队列 3.4获取队列头部元素 3.5 获取队列队尾元素 3.6 获取队列中有效元素个数 3.7检测队列是否为空 3.8销毁队列 4.环形队列 4.1环形队列概念 …

java环形队列_数组实现环形队列Java

用数组实现环形队列的特点是高效。 能快速判断队列是否 满/空&#xff1b; 能快速存取数据。 因为简单高效&#xff0c;所以甚至在硬件中都实现了环形队列。 环形队列广泛应用于网络数据的收发&#xff0c;和不同应用间数据交换(内核和应用程序大量交换数据&#xff0c;从硬件接…

环形队列初步探讨

文章目录 前言一、环形队列二、环形队列基本操作二、示例总结 前言 最近使用队列的时候&#xff0c;在实现大数据整体平移的时候还是用了内存copy虽然暂时达到性能要求&#xff0c;但是总感觉很笨重&#xff0c;最后用环形队列重构了部分代码&#xff0c;效果还行。 一、环形队…

环形队列

介绍 环形队列是队列的一种特殊情况&#xff0c;也是基于队列的实现&#xff0c;队列是动态的集合&#xff0c;而环形队列则是固定长度的&#xff0c;当队列满时&#xff0c;则从队首删除元素。其原理基本和队列一致&#xff0c;都是实现先进先出的策略。 实现 先定义数据&…

C语言,环形队列

什么是环形队列&#xff1f; 环形缓冲区是一个非常典型的数据结构&#xff0c;这种数据结构符合生产者&#xff0c;消费者模型&#xff0c;可以理解它是一个水坑&#xff0c;生产者不断的往里面灌水&#xff0c;消费者就不断的从里面取出水。 那就可能会有人问&#xff0c;既然…

数据结构(10)---队列之环形队列

环形队列 文章目录 环形队列什么是环形队列循环队列的实现第一种实现第二种实现 什么是环形队列 环形队列也是队列的一种数据结构, 也是在队头出队, 队尾入队; 只是环形队列的大小是确定的, 不能进行一个长度的增加, 当你把一个环形队列创建好之后, 它能存放的元素个数是确定的…

数据结构--环形队列的介绍与实现

数据结构--环形队列实现 一、环形队列实现原理环形队列的几个判断条件 二、代码实现1.环形队列类&#xff08;CircleQueue&#xff09;2.环形队列类测试类3.程序运行结果4.完整代码 环形队列可以用数组实现&#xff0c;也可以使用循环链表实现.在使用数组实现循环队列的时候&am…

【数据结构(C语言描述)】环形队列

目录 一、基础知识二、数组实现环队2.1 初始化2.2 判断环队是否为空2.3 判断环队是否为满2.4 入队2.5 出队2.6 取队头元素2.7 取队尾元素2.8 销毁环队 三、链表实现环队3.1 初始化3.2 判断环队是否为空3.3 判断环队是否为满3.4 入队3.5 出队3.6 取队头元素3.7 取队尾元素3.8 销…

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

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

Mysql 死锁和死锁的解决方案

最近在研究Mysql底层原理&#xff0c;研究到了死锁&#xff0c;感觉挺有意思&#xff0c;在这里和大家分享一下 前置知识&#xff1a;需要了解锁的种类&#xff0c;如表锁、行锁&#xff1b;行锁又分为记录锁、间隙锁、临键锁等等&#xff1b;什么情况下会加表锁&#xff0c;什…

mysql死锁演示

背景&#xff1a; 线上日志突然爆了有数据库死锁的日志。 通过以下语句查询数据库死锁的日志 SHOW ENGINE INNODB STATUS 通过 日志分析&#xff0c;看到了两条update语句并且是里面有子查询。还有两个表的更新顺序问题。 解决方案是&#xff1a;加了分布式锁&#xff0c;…

记录一次mysql死锁

一&#xff0c;死锁发现 项目中有一个接口包含更新操作1&#xff0c;后面发现更新失败&#xff0c;通过查看应用程序日志&#xff0c;发现发生了死锁 sql 1 如下 1.最初版本根据id为条件&#xff0c;更新&#xff08;plan_start_time 二级索引&#xff09; update tt_task …

深入MySQL死锁场景

总结死锁需满足以下条件&#xff1a; 2个或者2个以上的并发事务操作并发事务之间存在锁冲突锁冲突关系成环形 GAP锁和Insert的隐式锁&#xff0c;最容易导致死锁&#xff0c;以下分析从这俩典型场景开始。 1. 表结构 建立以下表作为场景验证&#xff0c;id为主键&#xff0…

mysql死锁查询语句

mysql 死锁:如何解决mysql死锁 可直接在mysql命令行执行&#xff1a;showengineinnodbstatus\G;查看造成死锁的sql语句&#xff0c;分析索引情况&#xff0c;然后优化sql然后showprocesslist;另外可以打开慢查询日志&#xff0c;linux下打开需在my.cnf的[mysqld]里面加上以下内…

MySQL死锁排查步骤

系列文章目录 第一章&#xff1a;sql_mode模式 第二章&#xff1a;optimize table、analyze table、alter table、gh-ost 第三章&#xff1a;InnoDB MVCC原理 第四章&#xff1a;sql语句执行过程 第五章&#xff1a;Percona Toolkit工具简介 第六章&#xff1a;MySQL索引 第七…

MySql死锁过程

死锁一般怎么导致呢&#xff0c; 抛开一堆概念&#xff0c; 我就把死锁当成死结。 就是你代码获取锁的顺序问题。 MySql的死锁和我们正常代码也一样&#xff0c; 都是互通的&#xff0c; 当你修改一个表的行数据的时候&#xff0c; 就需要对那一行数据进行加锁。 所以很容易想…

中秋遇到mysql死锁怎么办

文章目录 前言一、什么是死锁二、死锁的产生条件三、死锁示例四、死锁的分析和查看1.查看最近1个死锁信息2.查看正在运行中的事务信息3.查看加锁信息 五、死锁的内部处理方案1.死锁探测机制2.锁等待超时机制 六、手动释放锁1.表级锁手动释放2.行级锁手动释放 七、死锁的优化策略…

mysql 死锁分析

一、 什么是死锁 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去.此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进程称为死锁进程.二、 死锁产生的四个必要条件 1.互斥条件&#xff1a;指进…

MySQL死锁分析

背景知识 MySQL数据库InnoDB引擎的行级锁在使用时是在索引记录上加锁的。 行级锁从占有模式上分为&#xff1a; 排他锁&#xff1a;独占行数据&#xff0c;如某事务获取了该行记录的排他锁&#xff0c;其他事务在获取该记录的排他锁和共享锁时需等待&#xff1b;共享锁&…