【数据结构与算法】之队列详解

队列(Queue)是一种重要的线性数据结构,遵循先进先出、后进后出的原则。本文将更详细地介绍队列的概念、特点、Java 实现以及应用场景。
模运算小复习:
a % b 的值总是小于b
5 % 4 = 1 5 % 2 = 1
1 % 5 = 1 4 % 5 = 4
1. 队列概念概述
想象一下排队买票,先排队的人总是先买到票。队列就像这样,元素从一端进入,称为队尾(Rear)或尾指针(tail),从另一端取出,称为队首(Front)或头指针(head)。元素的添加操作称为入队(Enqueue)或加入队列,删除操作称为出队(Dequeue)或移出队列。

队列是一种抽象数据类型 (ADT),这意味着我们只关心它的操作和特性,而不关心具体的实现方式。队列的关键操作包括:
-
enqueue(item): 将元素 item 添加到队尾。
-
dequeue(): 移除并返回队首元素。
-
peek(): 返回队首元素,但不移除它。
-
isEmpty(): 检查队列是否为空。
-
size(): 返回队列中元素的数量 (部分实现可能不包含此方法,需要额外维护一个计数变量)。
2. 队列的特点
-
先进先出 (FIFO): 这是队列最核心的特点,最先入队的元素总是最先出队。
-
操作受限: 只能在队尾入队,在队首出队。这限制了访问元素的灵活性,但也保证了操作的高效性 (时间复杂度通常为 O(1))。
3. 队列的 Java 实现
Java 中可以使用链表或环形数组实现队列。
3.1 基于链表的实现

public class LinkedListQueue<T> {private Node<T> head; // 指向队首节点的指针private Node<T> tail; // 指向队尾节点的指针private int size; // 记录队列大小private static class Node<T> { // 链表节点的内部类T data;Node<T> next;Node(T data) {this.data = data;}}public LinkedListQueue() {this.head = null;this.tail = null;this.size = 0;}public boolean isEmpty() {return size == 0; // 或 head == null}public int size() {return size;}//入队public void enqueue(T item) {Node<T> newNode = new Node<>(item);if (isEmpty()) {head = newNode; // 如果队列为空,新节点既是队首也是队尾} else {tail.next = newNode; // 否则,将新节点添加到队尾}tail = newNode; // 更新队尾指针size++;}//出队public T dequeue() {if (isEmpty()) {throw new NoSuchElementException("Queue is empty"); // 使用更具体的异常类型}T data = head.data;head = head.next; // 更新队首指针if (head == null) { // 如果队列只有一个元素,出队后队列变空,tail 也需要置空tail = null;}size--;return data;}//返回队首元素public T peek() {if (isEmpty()) {throw new NoSuchElementException("Queue is empty");}return head.data;}
}
3.2 基于环形数组的实现

好处
-
对比普通数组,起点和终点更为自由,不用考虑数据移动
-
“环”意味着不会存在【越界】问题
-
数组性能更佳
-
环形数组比较适合实现有界队列、RingBuffer 等
public class ArrayQueue<T> {private T[] data;private int head; // 队首指针private int tail; // 队尾指针private int capacity; // 数组容量private int size; // 队列大小public ArrayQueue(int capacity) {this.capacity = capacity;this.data = (T[]) new Object[capacity];this.head = 0;this.tail = 0;this.size = 0;}public boolean isEmpty() {return size == 0; // 或 head == tail}public boolean isFull() {return size == capacity; // 使用 size 判断是否满}public int size() { return size; }//入队public void enqueue(T item) {if (isFull()) {//throw new RuntimeException("Queue is full");resizeArray(); // 扩容操作}data[tail] = item;tail = (tail + 1) % capacity; // 环形数组的关键:使用模运算size++;}//出队public T dequeue() {if (isEmpty()) {throw new NoSuchElementException("Queue is empty");}T item = data[head];data[head] = null; // 避免对象游离head = (head + 1) % capacity; // 环形数组的关键:使用模运算size--;return item;}//返回队首元素public T peek() {if (isEmpty()) {throw new NoSuchElementException("Queue is empty");}return data[head];}private void resizeArray() { // 扩容方法示例int newCapacity = capacity * 2;T[] newData = (T[]) new Object[newCapacity];for (int i = 0; i < size; i++) {newData[i] = data[(head + i) % capacity];}data = newData;head = 0;tail = size;capacity = newCapacity;}
}
4. 队列的基本操作图解
4.1 下标计算
例如,数组长度是 5,当前位置是 3 ,向前走 2 步,此时下标为 (3 + 2) % 5 = 0

-
cur 当前指针位置
-
step 前进步数
-
length 数组长度
4.2 判空
引入size属性后有两种方式:
return tail == size; //队列大小return size == 0;

4.3 判满
引入size属性后有两种方式:
return size = capacity; //数组容量return (tail + 1) % length == head;

4.4 入队
假设入队前队空:此时head == tail

入队后:head 不变,tail + 1,代码中这样书写 :tail = (tail + 1) % length,保证了不会越界的情况,不能设置为 tail ++ 。此时a即是队头也是队尾。

4.5 出队
这里要牢记队列的特点:先进先出、后进后出
假设此时a、b已入队,现在a要出队,出队前:a是队头,b是队尾

a出队后:此时b成为新队头

4.6 返回队头元素
略,具体实现:首先确保不是空队列,然后返回 data[head]
5. 各个操作的时间复杂度
-
入队 (enqueue): 数组实现: 均摊 O(1) (因为需要考虑扩容的情况,但大多数情况下是 O(1)), 链表实现: O(1)
-
出队 (dequeue): O(1)
-
查看队首元素 (peek): O(1)
6. 队列的局限性
-
数组实现的假溢出: 使用环形数组实现时,虽然解决了普通数组的"一次性"问题,但仍然存在容量限制。需要仔细处理扩容操作。
-
固定大小:在某些实现中,队列的大小是固定的,这意味着一旦队列满了,就不能再添加新的元素,除非移除一些元素。这可能导致数据丢失或需要额外的逻辑来处理溢出。
-
性能问题:在基于数组的队列实现中,如果队列经常达到其最大容量,那么在队列的末尾添加元素可能需要数组的复制,这会带来额外的时间成本。虽然这个操作是偶尔发生的,但在高负载情况下可能会影响性能。
-
不适合随机访问:队列不支持随机访问,这意味着你不能直接访问队列中间的元素。如果你需要随机访问,可能需要使用其他数据结构,如数组或链表。
-
不适合实时系统:在实时系统中,队列可能不是最佳选择,因为队列的操作(入队和出队)可能需要等待,特别是在有大量并发操作时。
-
空间效率:在基于数组的实现中,即使队列中没有很多元素,数组也可能被预分配了较大的空间,这可能导致空间的浪费。
-
操作的局限性:队列只允许在队尾添加元素,在队头移除元素。如果需要在队列中间进行操作,队列可能不是最合适的选择。
-
并发问题:在多线程环境中,队列的操作需要同步,以避免竞态条件和数据不一致的问题。这可能需要额外的锁机制,从而影响性能。
-
不适合处理大量数据:如果需要处理大量数据,队列可能不是最佳选择,因为队列的操作可能会因为数据量大而变得缓慢。
-
不适合需要频繁插入和删除的场景:如果应用场景中需要频繁地在队列的中间进行插入和删除操作,队列可能不是最佳选择,因为这些操作在队列中是不允许的。
-
不适合需要多种访问模式的场景:如果应用需要多种不同的数据访问模式,如堆栈的后进先出(LIFO)特性,队列可能不足以满足需求。
7. 总结和应用场景
队列是一种简单但强大的数据结构,其 FIFO 特性使其在许多场景下都非常有用,例如:
-
任务调度: 操作系统中的任务调度通常使用队列来管理待执行的任务,保证先提交的任务先执行。例如打印队列,按照先来先服务的顺序打印文档。
-
宽度优先搜索 (BFS): 图算法中常用的宽度优先搜索算法使用队列来存储待访问的节点, ensuring that nodes closer to the starting node are visited first.
-
缓冲区: 在生产者-消费者模型中,队列可以作为缓冲区,平衡生产者和消费者的速度差异。生产者将数据放入队列,消费者从队列中取出数据。队列可以缓解生产和消费速度不匹配的问题,避免数据丢失或程序阻塞. 例如,网络请求中的缓冲区。
-
消息队列: 在分布式系统中,消息队列用于异步通信。发送方将消息放入队列,接收方从队列中取出消息。消息队列可以解耦发送方和接收方,提高系统的可靠性和可扩展性。 例如,Kafka, RabbitMQ.
理解队列的概念和实现对于程序员来说至关重要,它能帮助我们更好地设计和优化程序。
希望本文能帮助各位看官更好地理解队列这种重要的数据结构!下期见,谢谢~
相关文章:
【数据结构与算法】之队列详解
队列(Queue)是一种重要的线性数据结构,遵循先进先出、后进后出的原则。本文将更详细地介绍队列的概念、特点、Java 实现以及应用场景。 模运算小复习: a % b 的值总是小于b 5 % 4 1 5 % 2 1 1 % 5 1 4 % 5 4 1. 队列…...
python最新h5st4.9.1调用源码(2025-10-25)
废话不多说,直接上源码,需要技术支持的私。 一、调用js方法: # -*- coding: utf-8 -*- """ -------------------------------------------------Author: byc6352File: jdh5st.pyTime: 2024/10/25 08:03Technical Support:by…...
微软投资比特币:将总资产1%投资于BTC?股东投票决定最终结果!
随着比特币及其他加密货币在全球金融市场中的影响力不断增加,科技巨头微软(Microsoft)也开始考虑是否在其资产负债表上纳入比特币。根据近期提交给美国证券交易委员会(SEC)的文件,微软将在2024年12月10日举…...
vue中标签的ref和id的用法和区别优缺点
Vue 3 中 ref 和 id 的用法详解:区别、优缺点及使用场景 在 Vue 3 开发中,我们经常需要获取 DOM 元素或组件实例来进行交互。Vue 提供了 ref 和原生 HTML 属性 id 来实现这种操作。虽然 ref 和 id 都能标识并操作元素,但它们的使用方式、优缺…...
Python基础知识-文件篇
Python 的文件操作是指与文件进行交互的各种技术和方法,包括读取、写入、关闭文件等。以下是对 Python 文件操作的详细介绍: 打开文件 要进行文件操作,首先需要打开文件。Python 提供了内置的 open() 函数。 file open(example.txt, r) …...
MacOS 环境下 VSCode 的 C++ 环境搭建
MacOS 环境下 VSCode 的 C 环境搭建 编译器安装 编译器可以选择 Clang 或者 GCC,在 MacOS 上 Clang 的安装更为简单一些。 Clang(推荐) 打开终端输入命令, clang -v 查看是否已经安装。 如果已经安装,会输出类似于如下的信息࿱…...
WPF样式
WPF(Windows Presentation Foundation)是微软推出的一种用于构建Windows应用程序的UI框架。它提供了一套丰富的控件、图形和动画功能,允许开发者创建具有丰富视觉效果的现代用户界面。WPF中的样式(Styles)是一种强大的…...
Vue Router 如何配置 404 页面?
在 Vue 项目中,如果你想配置一个 404 页面(即找不到页面提示),你需要通过 Vue Router 来设置。这通常通过将路由配置中的 *(通配符)指向一个 404 组件来实现。 // 定义路由部分 const routes [{path: /,c…...
【C++:智能指针】
什么是内存泄漏 内存泄漏是指因为疏忽或者错误造成程序对一部分不再使用的内存没有进行释放的情况,内存释放不是指内存在物理上的消失,而是应用程序分配某段内存时,因设计错误,失去了对该内存的控制,从而造成内存浪费 …...
onlyoffice docker启用jwt并生成jwt
一、说明 本文是docker教程,linux/win的安装版本也类似,只需要修改配置文件中的secrt就可以了【Configuring JWT for ONLYOFFICE Docs - ONLYOFFICE】 二、正文开始 docker启动时候如果不想使用jwt,加上参数-e JWT_ENABLEDfalse就可以了&…...
希尔贝壳受邀参加首届“数据标注产业大会暨供需对接会”
为推动数据标注产业高质量发展,促进数据标注基地快速形成面向产业的规模化服务能力。10月22日,由国家数据局数字科技和基础设施建设司指导的首届“数据标注产业大会暨供需对接会”在北京召开,希尔贝壳受邀参加。 大会旨在进一步推动数据标注…...
35.第二阶段x86游戏实战2-C++遍历技能
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 本次游戏没法给 内容参考于:微尘网络安全 本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要…...
Jenkins发布vue项目,版本不一致导致build错误
问题一 yarn.lock文件的存在导致在自动化的时候,频频失败问题二 仓库下载的资源与项目资源版本不一致 本地跑好久的一个项目,现在需要部署在Jenkins上面进行自动化打包部署;想着部署后今后可以省下好多时间,遂兴高采烈地去部署&am…...
vue3使用webSocket
1.安装插件 npm i vueuse/core10.11.12.引入使用 import { useWebSocket } from "vueuse/core"const { send, open, close: wsClose, status } useWebSocket(ws://192.168.100.90:53021/inms-application/alarm, {onMessage: (ws, { data }) > {console.log(&q…...
957种卫星参数文档的分享下载
自1957年10月4日苏联发射第一颗人造卫星Sputnik-1至今已经有67年,如今卫星已经在气象、遥感和通讯等领域为我们提供服务。 现在为你分享957种卫星参数,需要Excel文档请在文未查看领取下载方式。 卫星介绍 卫星是由人类制造并发射到太空,围…...
负载均衡详解:背景、实现技术、作用范围与常用算法
负载均衡(Load Balancing)是一种通过将请求分配到多个服务器上,从而优化资源使用、提高响应速度并增强系统可靠性的一种技术手段。它是现代分布式系统和互联网应用中不可或缺的一部分。在本篇文章中,我们将深入探讨负载均衡的方方…...
CCAA:产品认证基础3(产品认证方案)
学习要点 *产品认证方案和认证制度 *产品认证方案的基本要素、功能和活动 *产品认证方案的类型 *产品认证方案的制订和实施 *质量管理体系在产品认证方案中的应用 *典型产品认证方案的应用 第一节 产品认证方案和产品认证制度 一、概念 认证制度是指实施认证的规则、程序和…...
go语言中的Scan()和Scanln()输入函数
Scan()输入函数 package mainimport "fmt"func main() {var a intvar b stringfor {fmt.Println("请输入一个整数和一个字符串(用空格分隔):")fmt.Scan(&a, &b) // 直接读取输入到变量中fmt.Println("整数…...
UML外卖系统报告(包含具体需求分析)
1 系统背景 随着互联网技术的快速发展,外卖订餐服务逐渐成为人们生活中的一部分。传统的电话订餐方式面临诸多不便和限制,而基于互联网的外卖订餐系统则提供了更加便捷、快速和高效的订餐服务。这种系统通过将餐厅、顾客和配送人员连接起来,…...
net Core Data Protection 数据保护 加密 编码 哈希 FromServices
》》》 通过构造函数 获取服务 [Route("api/[controller]")][ApiController]public class DataProtectController : ControllerBase{[HttpGet]public string Info(){return "zen";}// [FromServices] 自动获取 builder.Services.AddDataProtection()注…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...
欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!
多连接 BLE 怎么设计服务不会乱?分层思维来救场! 作者按: 你是不是也遇到过 BLE 多连接时,调试现场像网吧“掉线风暴”? 温度传感器连上了,心率带丢了;一边 OTA 更新,一边通知卡壳。…...
spring boot使用HttpServletResponse实现sse后端流式输出消息
1.以前只是看过SSE的相关文章,没有具体实践,这次接入AI大模型使用到了流式输出,涉及到给前端流式返回,所以记录一下。 2.resp要设置为text/event-stream resp.setContentType("text/event-stream"); resp.setCharacter…...
Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...
