【数据结构】队列的实现与优化指南
一、前言
队列是一种重要的数据结构,它按照“先入先出”(FIFO)的原则管理数据。本文将介绍队列的概念、应用场景,以及如何使用数组实现普通队列和环形队列。
二、内容
2.1 概述
(1)什么是队列?
队列(Queue)是一种常见的数据结构,它是一个线性数据结构,按照先入先出(FIFO,First-In-First-Out)的原则来管理数据。
注意,先入先出的原则就意味着最早进入队列的元素将最先被取出,而最后进入队列的元素将最后被取出,类似于排队等候服务的行为。
队列可以使用数组或链表来实现,具体实现方式因应用需求而异。
队列支持两种主要的操作,即入队(Enqueue)和出队(Dequeue)。
- 入队:将元素添加到队列的尾部。
- 出队:从队列的头部取出元素并删除它。
(2)应用场景
队列的应用场景有很多,比如:
- 任务调度:操作系统使用队列来管理待执行的任务或进程,确保按照进入队列的顺序分配处理时间。
- 数据缓冲:队列用于数据传输和处理中,特别是在异步通信或生产者-消费者模式中,可以缓冲待处理的数据。
- 广度优先搜索:在图论和搜索算法中,队列用于实现广度优先搜索,以逐层遍历图结构。
- 打印任务队列:打印机队列用于管理待打印的文档,确保按照顺序打印。
- 网页请求队列:Web服务器可以使用队列来处理收到的请求,以便有序响应客户端请求。
- 排队系统:在银行、餐馆、医院等场所,队列被用来管理等待服务的客户,确保服务按照先来先服务的原则。
- ......
队列在计算机科学和实际应用中非常有用,因为它们提供了一种有效的方法来管理和调度数据或任务,以确保按照特定的顺序进行处理。
2.2 数组模拟队列
下面,我们用数组来模拟一个简单的队列数据结构。
(1)队列类定义
首先给出类的定义:
class ArrayQueue {private int maxSize;private int front;private int rear;private int[] data;ArrayQueue(int queueMaxSize) {maxSize = queueMaxSize; // 队列的最大容量data = new int[maxSize]; // 存放队列的数据front = -1; // 指向队列头的前一个位置rear = -1; // 直接指向队列尾部}// ... 方法定义
}
在这里,ArrayQueue 是一个队列类,使用数组作为内部数据存储。它包括最大容量(maxSize)、队列头(front)、队列尾(rear)和一个整数数组(data)来存放队列的数据。
构造函数 ArrayQueue 接受一个整数参数 queueMaxSize,表示队列的最大容量。初始化时,队列的头(front)和尾都(rear)被置为-1,表示队列为空。
需要注意这里的定义,在这里,front 变量指的是指向队列首元素的前一个位置,而 rear 变量则指向队列的尾部元素,即最后一个元素。
因此,初始队列的结构图如下:

(2)isEmpty
public boolean isEmpty() {return rear == front;
}
isEmpty() 方法用于检查队列是否为空。如果队列头和队列尾相等,表示队列中没有数据,返回 true;否则返回 false。
(3)isFull
public boolean isFull() {return rear == maxSize - 1;
}
isFull() 方法用于检查队列是否已满。如果队列尾等于最大容量减1,表示队列已满,返回 true;否则返回 false。
(4)enQueue
// 入队操作,添加数据到队尾
public void enQueue(int num) {if(isFull()) {System.out.println("队列已满,无法入队");return;}rear++;data[rear] = num;
}
enQueue 方法用于将数据添加到队列的尾部。首先,它会检查队列是否已满,如果是,将输出一条错误消息并不执行入队操作。如果队列未满,将 rear 后移,然后将数据存入队列尾部。
再次强调一下,这里的 rear 变量用于指向队列的最后一个数据,即队列的尾部。

(5)deQueue
// 出队操作,取出队头数据
public int deQueue() {if(isEmpty()) {throw new RuntimeException("队列为空,无法出队"); }front++;return data[front];
}
deQueue 方法用于取出队列头部的数据。首先,它会检查队列是否为空,如果是,将抛出一个运行时异常。如果队列不为空,将 front 后移,然后返回队头的数据。
再次强调一下,这里的 front 变量指向的是队列头数据的前一个位置。

(6)headQueue
// 查看队头数据(注意不是取出数据)
public int headQueue() {if(isEmpty()) {throw new RuntimeException("队列为空,没有数据");}return data[front+1];
}
headQueue 方法用于获取队列头部的数据,但不会将其出队。它会检查队列是否为空,如果是,将抛出一个运行时异常。如果队列不为空,将返回队头的数据。
(7)showQueue
// 打印队列
public void showQueue() {if(isEmpty()) {System.out.println("队列为空,没有数据");return;}// 简单的遍历队列for(int i = 0; i < data.length; i++) {System.out.printf("data[%d] = %d\n", i, data[i]);}
}
showQueue 方法用于简单地打印队列的所有元素。如果队列为空,将输出一条消息表示队列为空。否则,它会简单地遍历队列,打印每个数据元素的索引和值。
2.3 数组模拟环形队列
(1)存在的问题
我们再来思考一个问题,虽然上述的队列类实现了一个简单的队列数据结构,但仍然存在弊端。那就是数组使用一次后不能复用。
什么意思?
具体的,我们可以发现,每当队列入队一个数据,rear 变量就会往后移一位。每当有元素出队,front 变量也会往后移一位。但是!一旦 rear 变量到达队列的尾部,如果队列头部仍有空余的空间,就像这样:

那么此时根据 isFull() 方法的判断下,该队列是满的。因此无法再入队。
因此我们说,对于之前的队列简单实现来说,一旦队列中的数据元素被取出,对应的数组位置就不能再次使用。数据从头部添加,从尾部取出。一旦数组被填满,我们无法再添加新的数据,即使队列的前面已经有一些位置被释放出来。这就会导致内存资源浪费。
为了解决这个问题,我们考虑使用环形队列来优化。
那什么是环形队列?
事实上,环形队列是一种更高效的队列实现方式,它允许队列在达到最大容量后继续添加元素,以覆盖掉队列头部已经被取出的数据,实现数据的循环复用。
我们通过取模运算 % 来实现环形队列。
(2)思路分析
当我们考虑了队列内部数据存储资源的复用后,我们就需要对 front 和 rear 变量的含义进行一个的调整(当然不调整也行,看个人习惯)。
具体如下:
front变量:- 表示指向队列的第一个元素,即首元素。
data[front]是队列的第一个元素。front的初始值为 0。
rear变量:- 表示指向队列最后一个元素的下一个位置。
- 这是为了表示队列中哪些位置是可用的,以便继续添加新的元素。
rear的初始值同样为 0。
当我们这样约定好了后,就可以开始着手编写代码,得到一个环形队列。
此时判断队列已满或空时,逻辑需要略微调整。
判断环形队列空时,条件为:(rear == front)。因为当 rear 指针等于 front 指针时,表示队列没有有效的元素,即队列为空。
判断环形队列满时,条件为:(rear + 1) % maxSize == front
这该怎么理解?
事实上,在含义调整后,环形队列中的 rear 变量指向的位置实际上就是预留给下次入队的数据存放的位置。
当有一个新的数据入队时,rear 指向的位置就可以存储本次入队的数据的值,然后,rear 就会加一并取余 maxSize ,用于寻找下一个可以存储入队数据的位置。
因此,当(rear + 1) % maxSize 的值刚好等于 front,那么证明该环形队列已经满了,没有地方可以存储下一次入队的值。
举一个例子,假设
maxSize为 3,初始时front和rear都是0:
- 队列为空:
front = 0,rear = 0- 插入一个元素:
front = 0,rear = 1- 插入第二个元素:
front = 0,rear = 2- 插入第三个元素:
front = 0,rear = 0(此时队列满,因为(rear + 1) % maxSize等于front)- 取出第一个元素:
front = 1,rear = 0(此时队列有效元素个数为 2,因为(0+3-1) % 3 == 2)
示意图如下:

(3)优化后的队列类
优化后的代码实现如下:
class CircleArrayQueue {private int maxSize;private int front; // 初始值为 0,指向队头数据,即首元素private int rear; // 初始值为 0,指向队尾数据的下一个位置private int[] data;ArrayQueue(int queueMaxSize) {maxSize = queueMaxSize; data = new int[maxSize];}// 判断队列是否为空public boolean isEmpty() {return rear == front;}// 判断队列是否满public boolean isFull() {return (rear + 1) % maxSize == front;}// 入队:添加数据到队尾public void enQueue(int num) {if(isFull()) {System.out.println("队列已满,无法入队");return;}data[rear] = num;rear = (rear + 1) % maxSize;}// 出队,取出队头数据public int deQueue() {if(isEmpty()) {throw new RuntimeException("队列为空,无法出队"); }int value = data[front];front = (front + 1) % maxSize;return value;}// 显示队列的头数据(不是取出数据)public int headQueue() {if(isEmpty()) {throw new RuntimeException("队列为空,没有数据");}return data[front];}// 返回环形队列当前的元素个数public int size() {return (rear + maxSize - front) % maxSize;}// 打印队列public void showQueue() {if(isEmpty()) {System.out.println("队列为空,没有数据");return;}// 遍历思路,从 data[front] 遍历到 data[front + size]for(int i = front; i < front + size(); i++) {System.out.printf("data[%d] = %d\n", i%maxSize, data[i%maxSize]);}}
}
三、总结
本文详细介绍了队列数据结构的概念和应用,包括普通队列和环形队列的实现。队列是一种有序的数据结构,它在计算机科学中被广泛应用,用于管理数据和任务的顺序执行。普通队列使用数组实现,但存在内存资源浪费的问题。为了解决这个问题,我们引入了环形队列的概念,它允许队列数据的循环复用,更加高效地利用内存。
相关文章:
【数据结构】队列的实现与优化指南
一、前言 队列是一种重要的数据结构,它按照“先入先出”(FIFO)的原则管理数据。本文将介绍队列的概念、应用场景,以及如何使用数组实现普通队列和环形队列。 二、内容 2.1 概述 (1)什么是队列࿱…...
视频太大怎么压缩变小?三分钟学会视频压缩
随着科技的不断发展,视频已经成为了我们日常生活中不可或缺的一部分,然而,大尺寸的视频文件常常会给我们带来诸多困扰,例如发送不便、存储空间不足等等,那么,如何将这些过大的视频文件压缩变小呢࿱…...
Rust 泛型
泛型 Generics泛型详解 使用泛型参数,有一个先决条件,必需在使用前对其进行声明: fn largest<T>(list: &[T]) -> T {该泛型函数的作用是从列表中找出最大的值,其中列表中的元素类型为 T。首先 largest<T> 对…...
STM32+2.9inch微雪墨水屏(电子纸)实现显示
本篇文章从硬件原理以及嵌入式编程等角度完整的介绍了墨水屏驱动过程,本例涉及的墨水屏为2.9inch e-Paper V2,它采用的是“微胶囊电泳显示”技术进行图像显示,其基本原理是悬浮在液体中的带电纳米粒子受到电场作用而产生迁移,从而改变显示屏各…...
Hadoop3教程(二十九):(生产调优篇)集群扩容及缩容(白名单与黑名单)
文章目录 (150)添加白名单(151)服役新服务器(152)服务器间数据均衡(153)黑名单退役服务器参考文献 这一章还算是比较重要的。 (150)添加白名单 白名单&#…...
NET7下用WebSocket做简易聊天室
NET7下用WebSocket做简易聊天室 步骤: 建立NET7的MVC视图模型控制器项目创建websocket之间通信的JSON字符串对应的实体类一个房间用同一个Websocketwebsocket集合类,N个房间创建websocket中间件代码Program.cs中的核心代码,使用Websocket聊…...
详解API基础知识
目录 什么是API: API 的设计原则包括: API 的开发流程包括以下几个步骤: API 的使用场景包括: API 的优势包括: 然而,API 也存在一些挑战和问题,例如: 什么是API: API(应用程…...
b树和b+树
二叉树和平衡二叉树 二叉树,每个节点支持两个分支的树结构,相比于单向链表,多了一个分支。 二叉查找树,在二叉树的基础上增加了一个规则,左子树的所有节点的值都小于它的根 节点,右子树的所有子节点都大于它…...
Linux 下 Java 安装字体方法
因上线访问图字体乱码了,因为在windows下设置的微软雅黑,linux默认是没有的,所以需要给jdk安装一个微软雅黑字体。按照步骤来,so easy! 1)首先找到windows下面的字体,不用去其他地方下了&#…...
敏捷开发的实施要素和实现敏捷的实际改进
敏捷开发的实施要素如下: 个体和交互:胜过过程和工具。可以工作的软件:胜过面面俱到的文档。客户合作:胜过合同谈判。响应变化:胜过遵循计划。 敏捷开发过程是一个增量的、迭代的过程,责任人、开发人…...
学会使用Pandas进行数据清洗
大家好,如果你对数据科学感兴趣,那么数据清洗可能对你来说是一个熟悉的术语,本文将向你介绍使用Pandas进行数据清洗的过程。我们的数据通常来自多个资源,而且并不干净,它可能包含缺失值、重复值、错误或不需要的格式等…...
Stable Diffusion WebUI扩展a1111-sd-webui-tagcomplete之Booru风格Tag自动补全功能详细介绍
安装地址 直接附上地址先: Ranting8323 / A1111 Sd Webui Tagcomplete GitCodeGitCode——开源代码托管平台,独立第三方开源社区,Git/Github/Gitlabhttps://gitcode.net/ranting8323/a1111-sd-webui-tagcomplete.git上面是GitCode的地址,下面是GitHub的地址,根据自身情…...
Linux中iostat命令
iostat命令是IO性能分析的常用工具,其是input/output statistics的缩写。 一、安装 yum install sysstat -y二、参数说明 -c: 显示CPU使用情况-d: 显示磁盘使用情况--dec{ 0 | 1 | 2 }: 指定要使用的小数位数,默认为 2-g GROUP_NAME { DEVICE [...] | A…...
Pandas数据处理分析系列3-数据如何预览
Pandas-数据预览 Pandas 导入数据后,我们通常需要对数据进行预览,以便更好的进行数据分析。常见数据预览的方法如下: ①head() 方法 功能:读取数据的前几行,默认显示前5行 语法结构:df.head(行数) df1=pd.read_excel("销售表.xlsx",sheet_name="手机销…...
【汇编语言-王爽】第二章:寄存器
知识点 (一)寄存器 一个典型的CPU由运算器、控制器、寄存器等器件构成,这些器件靠内部总线相连。8086CPU有14个寄存器:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。其中AX、BX、CX、DX为通用寄存器,可…...
5G学习笔记之5G频谱
参考:《5G NR通信标准》1. 5G频谱 1G和2G移动业务的频段主要在800MHz~900MHz,存在少数在更高或者更低频段;3G和4G的频段主要在450MHz ~ 6GHz;5G主要是410MHz ~ 6GHz,以及24GHz ~ 52GHz。 5G频谱跨度较大,可…...
CSS 浮动布局
本文参考 https://blog.csdn.net/ZhangJiWei_2019/article/details/114669722 文档流简介 正常文档流 正常文档流,又称为“普通文档流”或“普通流”,也就是W3C标准所说的“normal flow”。 我们先来看一下正常文档流的简单定义:正常文档…...
CentOS 系统安装和使用Docker服务
系统环境 使用下面的命令,可以查看CentOS系统的版本。 lsb_release -a结果: 说明我的系统是7.9.2009版本的 安装Docker服务 依次执行下面的指令: yum install -y yum-utilsyum install -y docker即可安装docker服务 如果这样安装不成功…...
Docker-镜像的备份迁移及私有仓库的搭建
一、Docker-备份与迁移 A服务器系统配置 B服务器系统配置 1.用命令将容器保存为镜像。 案例,将A服务器的Docker容器迁移到另外一台服务器B,A服务器的容器配置过对应的文件,不想在B服务器重新搭建,可以使用该案例。 docker c…...
SQL数据库管理工具RazorSQL mac中文版特点与功能
RazorSQL mac是一款功能强大的SQL数据库管理工具,它支持多种数据库,包括MySQL、Oracle、Microsoft SQL Server、SQLite、PostgreSQL等。 RazorSQL mac 软件特点和功能 多种数据库支持:RazorSQL支持多种数据库,用户可以通过一个工…...
从splrep到splev:深入SciPy样条插值底层,看懂tck三元组,实现自定义插值控制
从splrep到splev:掌握SciPy样条插值的底层控制艺术 在数据科学和工程计算领域,插值技术就像一位隐形的调音师,能够将离散的数据点转化为流畅的曲线。当大多数用户满足于interp1d这类"一键式"解决方案时,真正的高手已经开…...
S32K148实战:用FlexCAN的RxFIFO+中断搞定多路CAN数据接收(附避坑点)
S32K148 FlexCAN实战:RxFIFO与中断机制的高效数据接收方案 在车载电子和工业控制领域,CAN总线作为可靠的通信骨干,其数据处理效率直接影响系统实时性。当面对多节点、高负载的CAN网络时,传统轮询方式往往力不从心。NXP S32K148微控…...
从康托集这个‘怪胎’出发,逆向理解Borel集、Sigma代数与拓扑空间的层层递进关系
从康托集逆向拆解:Borel集、σ-代数与拓扑空间的认知革命 数学分析中那些看似抽象的概念,往往藏着一个反常识的入口。1883年由德国数学家格奥尔格康托提出的康托集(Cantor Set),就是这样一个充满矛盾的存在——它既是勒…...
89张电力供应线路黑匣子目标检测数据集-包含完整原始图像与YOLO格式标注-适用于电力系统运维自动化与智能电网故障预警
电力供应线路黑匣子目标检测数据集分析 引言与背景 在电力系统运维与安全监测领域,黑匣子作为记录关键运行数据的重要设备,其准确识别与定位对于保障电力供应稳定性具有重要意义。本数据集专注于电力供应线路黑匣子的目标检测任务,提供了高…...
从图像模糊到语音识别:卷积在AI中的实战应用与Python代码示例
从图像模糊到语音识别:卷积在AI中的实战应用与Python代码示例 卷积运算在人工智能领域扮演着至关重要的角色,它不仅是计算机视觉和语音处理的基础,更是现代深度学习架构的核心组件。对于希望将理论知识转化为实际应用的开发者而言,…...
快速预览Office文档终极指南:无需安装Microsoft Office的轻量级解决方案
快速预览Office文档终极指南:无需安装Microsoft Office的轻量级解决方案 【免费下载链接】QuickLook.Plugin.OfficeViewer Word, Excel, and PowerPoint plugin for QuickLook. 项目地址: https://gitcode.com/gh_mirrors/qu/QuickLook.Plugin.OfficeViewer …...
4月21日发布!OPPO Pad Mini 要给小平板正名了
4月21日19:00,OPPO将召开新品发布会,除了Find X9s Pro等旗舰手机,最让我期待的就是OPPO Pad Mini这款小平板。说实话,这几年我一直觉得小平板是“鸡肋”——手机屏幕越做越大,折叠屏又能兼顾大屏,8.8英寸的…...
Bootstrap4 导航栏
Bootstrap4 导航栏 概述 Bootstrap4 是一个流行的前端框架,它提供了丰富的组件和工具来帮助开发者快速构建响应式、移动优先的网页。在Bootstrap4中,导航栏是一个重要的组件,用于在网页上创建顶部导航菜单。本文将详细介绍Bootstrap4导航栏的用法、样式和定制选项。 导航…...
VirtIO-GPU 指令流
VirtIO-GPU 指令流是虚拟机(Guest)与宿主机(Host)之间传输图形渲染命令的序列化字节流,基于 VirtIO 协议,分为 2D 控制指令流与 3D 渲染指令流(VirGL/Venus),通过 VirtQu…...
SAP ABAP开发避坑指南:BP业务伙伴的地址、银行、角色BAPI到底该怎么选?
SAP ABAP开发实战:BP业务伙伴BAPI选择策略与避坑技巧 每次打开SE37准备调用BP相关BAPI时,那些以BAPI_BUPA_开头的函数列表总让人眼花缭乱。上周刚踩过一个坑——用BAPI_BUPA_ADDRESS_CHANGE更新地址时,系统莫名其妙清空了邮政编码后三位。后来…...
