【数据结构】队列的实现与优化指南
一、前言
队列是一种重要的数据结构,它按照“先入先出”(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支持多种数据库,用户可以通过一个工…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
02-性能方案设计
需求分析与测试设计 根据具体的性能测试需求,确定测试类型,以及压测的模块(web/mysql/redis/系统整体)前期要与相关人员充分沟通,初步确定压测方案及具体的性能指标QA完成性能测试设计后,需产出测试方案文档发送邮件到项目组&…...
Qt学习及使用_第1部分_认识Qt---Qt开发基本流程
前言 学以致用,通过QT框架的学习,一边实践,一边探索编程的方方面面. 参考书:<Qt 6 C开发指南>(以下称"本书") 标识说明:概念用粗体倾斜.重点内容用(加粗黑体)---重点内容(红字)---重点内容(加粗红字), 本书原话内容用深蓝色标识,比较重要的内容用加粗倾…...