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

队列(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.

理解队列的概念和实现对于程序员来说至关重要,它能帮助我们更好地设计和优化程序。

希望本文能帮助各位看官更好地理解队列这种重要的数据结构!下期见,谢谢~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

windows|常见的文件伪装方法

几种常见的文件伪装方法&#xff1a; 扩展名伪装unicode字符伪装压缩包伪装隐写术 方法仅限于学习目的&#xff0c;不用于任何恶意或非法用途。 ———— 一、扩展名伪装&#xff1a;假装是另一种类型的文件 修改文件的扩展名&#xff0c;使得文件看起来像其他类型的文件&a…

取消element-ui中账号和密码登录功能浏览器默认的填充色,element-ui登录账号密码输入框禁用浏览器默认填充色问题

标题 问题展示 修改后 <div class="loginForm"><el-formref="formB":model="formDataB":rules="rulesB"class="login-form"label-position="left"><el-form-item prop="userNo" clas…

CentOS 7(Linux)详细安装教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 一、CentOS镜像的下载&#xff08;准备工作&#xff09; 我选择的是其他镜像源的下载地址&#xff1a; Index of /centos-vault/7.6.1810/isos/x86_64/ | 南阳理工学院开源镜…

u盘装win10系统提示“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”解决方法

我们在u盘安装原版win10 iso镜像时&#xff0c;发现在选择硬盘时提示了“windows无法安装到这个磁盘,选中的磁盘采用GPT分区形式”&#xff0c;直接导致了无法继续安装下去。出现这种情况要怎么解决呢&#xff1f;下面小编分享u盘安装win10系统提示“windows无法安装到这个磁盘…

Android Gradle

#1024程序员节&#xff5c;征文# Gradle 是一款强大的自动化构建工具&#xff0c;广泛应用于 Android 应用开发。它通过灵活的配置和丰富的插件系统&#xff0c;为项目构建提供了极大的便利。本文只是简单的介绍 Gradle 在 Android 开发中的使用&#xff0c;包括其核心概念、构…

智联招聘×Milvus:向量召回技术提升招聘匹配效率

01. 业务背景 在智联招聘平台&#xff0c;求职者和招聘者之间的高效匹配至关重要。招聘者可以发布职位寻找合适的人才&#xff0c;求职者则通过上传简历寻找合适的工作。在这种复杂的场景中&#xff0c;我们的核心目标是为双方提供精准的匹配结果。在搜索推荐场景下&#xff0c…

【分立元件】电阻的额定电压和最高电压

在文章:【分立元件】贴片电阻的额定功率中我们讲到使用电阻器时,不仅要注意额定功率,还要注意电压相关的一些项目。 本文我们将对与电阻基本参数关联的额定电压和元件最高电压这两个术语及其定义(包括它们之间的关系)进行解说。 额定电压 如下所示国巨片式电阻规…

ARM学习(33)英飞凌(infineon)PSOC 6 板子学习

笔者来聊一下psoc62 系列板子的知识 1、PSOC62板子介绍 Psoc6-evaluationkit-062S2 与RT-Thread联合推出的一款32位的双core的板子&#xff0c;基于CortexM4以及CortexM0。 管脚兼容Arduio。板载DAP-Link&#xff0c;可以支持调试以及串口&#xff0c;无需外接2MB的Flash以及…

Java 开发——(下篇)从零开始搭建后端基础项目 Spring Boot 3 + MybatisPlus

上篇速递 - Spring Boot 3 MybatisPlus 五、静态资源访问 1. 基础配置 在 Spring Boot 中访问静态资源非常方便。Spring Boot 默认支持从以下位置加载静态资源&#xff1a; /META-INF/resources//resources//static//public/ 这些目录下的文件可以直接通过 URL 访问。 例…

【python实操】python小程序之参数化以及Assert(断言)

引言 python小程序之参数化以及Assert&#xff08;断言&#xff09; 文章目录 引言一、参数化2.1 题目2.2 代码2.3 代码解释 二、Assert&#xff08;断言&#xff09;2.1 概念2.1.1 Assert语句的基本语法&#xff1a;2.1.2 基本断言2.1.3 断言函数参数2.1.4 断言前后状态一致 2…

【计网】从零开始认识IP协议 --- 理解网段划分,NAT策略,私有IP和公网IP,认识公网

任何收获都不是偶然&#xff0c; 一点一滴的进步终会让未来的你焕然一新&#xff01; 从零开始认识IP协议 1 为什么要进行网段划分2 特殊IP地址与数量限制3 私有IP和公网IP4 彻底理解网段划分5 认识公网 1 为什么要进行网段划分 我们以一个例子来讲解为什么要进行网段划分&a…

Java【多线程】单例模式

目录 单例模式 饿汉模式 懒汉模式 懒汉模式-多线程版 单例模式 单例模式是一种设计模式 设计模式相当于棋谱 棋谱&#xff0c;大佬把一些对局整个推演过程&#xff0c;写出来 设计模式&#xff0c;是属于程序员的棋谱 单例模式&#xff08;单个实例/对象&#xff09;&…

sqli-labs靶场安装以及刷题记录-docker

sqli-labs靶场安装以及刷题记录-docker sqli-labs靶场安装-dockersqli-labs靶场刷题less-1 单引号less-2 数字型less-3 单引号括号less-4 双引号括号less-5 单引号布尔盲注less-6 双引号布尔盲注less-7 单引号加括号、输出到文件less-8 单引号布尔盲注less-9 单引号时间盲注les…

背景动态变化的html页面

首先看下效果图&#xff1a; 把下面的代码保存到 .html 结尾的文件里&#xff0c;用浏览器打开即可。 <!DOCTYPE html> <html> <head><title>动态背景</title><style>/* 样式表 */body {height: 100vh;display: flex;align-items: cente…

基于GA遗传优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2022a 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff09…

NLP--一起学习Word Vector【实践】

纸上得来终觉浅&#xff0c;绝知此事要躬行。 《冬夜读书示子聿》 值此1024的程序员节&#xff0c;我们一起学习 Word Vector。 本章一起学习文本向量化&#xff0c;掌握文本向量的相关概念&#xff0c;了解各个文本向量&#xff0c;实现文本向量的算法 我开启了一个NLP共学坊…

echarts散点图

一、类似散点图折线图不展示折线 option {grid: {left: 10,right: 20,top: 35,bottom: 15,containLabel: true},tooltip: {show: true,trigger: item,backgroundColor: "rgba(0,0,0,0)", // 提示框浮层的背景颜色。formatter: function (params) {var html <d…

洞见数据未来,StarRocks Summit Asia 2024 即将启幕!

在 AI 时代&#xff0c;我们需要怎样的数据基础软件&#xff1f; 数据量和数据类型的需求飞速上涨&#xff0c;我们不仅需要将历史上各种基础设施中的数据进行分析使用&#xff0c;还要关注性能、灵活性、性价比&#xff0c;以及确保单一可信数据源。这一切构成了当前大数据领…

【实战案例】Django框架表单处理及数据库交互

本文基于之前内容列表如下&#xff1a; 【图文指引】5分钟搭建Django轻量级框架服务 【实战案例】Django框架基础之上编写第一个Django应用之基本请求和响应 【实战案例】Django框架连接并操作数据库MySQL相关API 【实战案例】Django框架使用模板渲染视图页面及异常处理 更新编…

【python实战】利用代理ip爬取Alibaba海外版数据

引言 在跨境电商的业务场景中&#xff0c;数据采集是分析市场、了解竞争对手以及优化经营策略的重要环节。然而&#xff0c;随着越来越多企业依赖数据驱动决策&#xff0c;许多跨境电商平台为了保护自身数据&#xff0c;采取了更严格的防护措施。这些平台通过屏蔽大陆IP地址或部…