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

⭐作者介绍:大二本科网络工程专业在读,持续学习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]);
        }
    }

    @Override
    public 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) - 1
    public int[] array; //数组实现队列

    /**
     * 初始化构造器。此处有一个JavaSE的知识:声明了有参构造器,则自动缺失了无参构造器
     */
    public CircleQueueArray(int maxSize) {
        //front和rear不需要初始化,在环形队列的实现中默认为0
        this.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默认为0
            int 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]);
        }
    }

    @Override
    public String toString() {
        return "CircleQueueArray{" +
                "maxSize=" + maxSize +
                ", front=" + front +
                ", rear=" + rear +
                ", array=" + Arrays.toString(array) +
                '}';
    }
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

淘宝/天猫店铺订单数据导出、销售报表、数据分析

最近有厂商提出想把天猫店铺的数据拿到后台ERP管理系统中&#xff0c;并能实现线下打印电子面单功能。接手这个需求按照度娘给的指引&#xff0c;申请天猫开发者帐号&#xff0c;但是。。。大厂把订单传送接口关了&#xff0c;只对厂商自研软件开放&#xff0c;还需要租用聚石塔…

「MongoDB」时序数据库和MongoDB第二部分-模式设计最佳实践

在上一篇博客文章时间序列数据与MongoDB&#xff1a;第一部分-简介中&#xff0c;我们介绍了时间序列数据的概念&#xff0c;然后介绍了一些可以用于帮助收集时间序列应用程序需求的发现问题。对这些问题的回答有助于指导支持大容量生产应用程序部署所需的模式和MongoDB数据库配…

[牛客101] 二叉树的层序遍历

这道题会考察很多知识点,这里专门进行详解 文章目录题目描述二. 题目分析完整代码题目描述 二. 题目分析 首先,我们会想到存储方式为二维数组.数组每一行存储一层的结点.怎么确定每一行要存储几个结点呢.由于节点与节点之间存在父子关系,所以,在存储某一层的结点时,就可以通过…

Python图像处理【12】基于小波变换执行图像去噪

基于小波变换执行图像去噪0. 前言1. 小波变换基础2. 小波变换去噪原理3. 使用 pywt 执行小波变换图像去噪4. 使用 scikit-image 执行小波变换图像去噪4.1 循环旋转技术4.2 改进图像去噪质量小结系列链接0. 前言 小波 (wavelets) 变换是表示和分析多分辨率图像的通用方法&#…

栈的实现及相关OJ题

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点!人生格言&#xff1a;当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔&#x1f9be;&am…

再摘一枚重要奖项!腾讯安全获得云安全联盟CSA 2022安全金盾奖

4月13日&#xff0c;第六届云安全联盟大中华区大会&#xff08;CSA GCR Congress&#xff09;在上海举办&#xff0c;大会由联合国数字安全联盟、上海市经济和信息化委员会、上海市委网络安全和信息化委员会办公室、上海市普陀区人民政府指导&#xff0c;云安全联盟大中华区主办…

vue面试题2023

1.$route和$router的区别? routes : 数组。 路由匹配规则 router &#xff1a; 对象。 路由对象 $router &#xff1a; 对象。 用于跳转路由 和 传递参数 $route &#xff1a;对象。 用于接收路由跳转参数 1.Vue的生命周期方法有哪些&#xff1f; - beforeCreate 初始化实…

【产品应用】一体化步进伺服电机在高速异形插件机的应用

随着科技的不断发展&#xff0c;自动化生产设备在各个行业中得到了广泛的应用。高速异形插件机作为自动化生产设备中的一种&#xff0c;其核心部件之一就是一体化步进伺服电机。本文将详细介绍一体化步进伺服电机在高速异形插件机中的应用。 01.设备简介 高速异形插件机是一种…

用智能手机拍的模糊照片怎么办?学会这个技巧让它变得清晰

智能手机的相机功能越来越强大&#xff0c;但有时候我们还是会拍出一些模糊的照片。这可能是因为手抖或者光线不足等原因导致的。但不要担心&#xff0c;有一些简单的技巧可以帮助您将模糊的照片变得更加清晰。 1.稳定手机 拍摄清晰照片的第一步是确保相机保持稳定。拍照时最…

【CSS】课程网站 Banner 制作 ② ( Banner 栏版心盒子测量 | Banner 版心盒子模型左侧导航栏代码示例 )

文章目录一、Banner 栏版心盒子测量1、测量版心元素尺寸2、课程表测量二、Banner 版心盒子模型左侧导航栏代码示例1、HTML 标签结构2、CSS 样式3、展示效果一、Banner 栏版心盒子测量 1、测量版心元素尺寸 拉四条辅助线 , 将版心包起来 , 可以测量 Banner 条版心的尺寸为 1200 …

Cacti监控远程linux机器配置(被监控端)

一、被监控机安装snmp yum -y install snmp二、被监控机的配置 vi /etc/snmp/snmpd.conf做以下更改&#xff1a; 1、找到com2sec notConfigUser default public 改为&#xff1a;com2sec notConfigUser 192.168.1.1(改成监控服务器的ip) public 2、找到acce…

Pandas入门实践3 -数据可视化

人类大脑擅长于在数据的视觉表现中寻找模式;因此在这一节中&#xff0c;我们将学习如何使用pandas沿着Matplotlib和Seaborn库来可视化数据&#xff0c;以获得更多的特性。我们将创建各种可视化&#xff0c;帮助我们更好地理解数据。 使用pandas绘图 我们可以使用plot()方法创…

【linux】Ubuntu aarch64编译安装RXTX进行串口通信

目录1.下载RXTX2.源码下载方式一&#xff1a;方式二&#xff1a;3. 编译源码4.编译源码时遇到的问题问题1&#xff1a;./configure command not found问题2&#xff1a;error: UTS_RELEASE undeclared问题3&#xff1a;libtool: install: armv6l-unknown-linux-gnu/librxtxRS48…

【ZUUL2踩坑】题一:Ribbon集成动态properties存在的原生风险

目录 一、问题背景 二、问题分析 1、配置文件空档期的问题 一、问题背景 JAVA的Properties工具有两种写配置文件的方式&#xff0c;一种是覆盖&#xff0c;一种是追加。 但是动态配置文件一般需要进行创建或更新&#xff0c;不会选择追加内容&#xff0c;所以只能选择进行配…

docker目录映射

docker 常用命令 docker ps // 查看所有正在运行容器 docker stop containerId // containerId 是容器的ID docker ps -a // 查看所有容器 $ docker ps -a -q // 查看所有容器ID docker stop $(docker ps -a -q) // stop停止所有容器 docker rm $(docker ps -a -q) // remove删…

replugin宿主与插件通信小结

近来replugin开发中遇到宿主和插件间需要通信的情形&#xff0c;思来只有进程间通信(IPC)才是比较好的宿主与插件的通信方式。而Android进程间通信主要有2种方式&#xff1a;Messenger和AIDL。 AIDL&#xff08;Android Interface Definition Language&#xff09;是Android接…

ChatGPT团队中,3个清华学霸,1个北大学霸,共9位华人

众所周知&#xff0c;美国硅谷其实有着众多的华人&#xff0c;哪怕是芯片领域&#xff0c;华为也有着一席之地&#xff0c;比如AMD 的 CEO 苏姿丰、Nvidia 的 CEO 黄仁勋 都是华人。 还有更多的美国著名的科技企业中&#xff0c;都有着华人的身影&#xff0c;这些华人&#xff…

Java入坑之类的派生与继承

一、继承 1.1继承的概念 Java中的继承&#xff1a;子类就是享有父类的属性和方法&#xff0c;并且还存在一定的属性和方法的扩展。 Subclass&#xff0c;从另一个类派生出的类&#xff0c;称为子类(派生类&#xff0c;扩展类等) Superclass&#xff0c;派生子类的类&#xff…

3.5 函数的极值与最大值和最小值

学习目标&#xff1a; 我要学习函数的极值、最大值和最小值&#xff0c;我会采取以下几个步骤&#xff1a; 理解基本概念&#xff1a;首先&#xff0c;我会理解函数的极值、最大值和最小值的概念。例如&#xff0c;我会学习函数在特定区间内的最高点和最低点&#xff0c;并且理…

( “树” 之 DFS) 104. 二叉树的最大深度 ——【Leetcode每日一题】

104. 二叉树的最大深度 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 示例&#xff1a; 给定二叉树 [3,9,20,null,null,15,7]&#xff0c; 返回它的最大深度 3 。 思路&am…