数据结构 - 队列

队列也是一种操作受限的线性数据结构,与栈很相似。

在这里插入图片描述

01定义

栈的操作受限表现为只允许在队列的一端进行元素插入操作,在队列的另一端只允许删除操作。这一特性可以总结为先进先出(First In First Out,简称FIFO)。这意味着在队列中第一个加入的元素将第一个被移除。

在这里插入图片描述

入****队:向队列中添加新元素的行为叫做入队;

出****队:从队列中移除元素的行为叫做出队;

队头:在队列中允许进行元素移除行为的一端称为队头;

队尾:在队列中运行进行元素添加行为的一端称为队尾;

空****队列:当队列中没有元素时称为空队列。

满****队列:当队列是有限容量,并且容量已用完,则称为满队列。

队列****容量:当队列是有限容量,队列容量表示队列可以容纳的最大元素数量。

队列****大小:表示当前队列中的元素数量。

在这里插入图片描述

02分类

队列根据存储方式和功能特性有两种分类方式。

1、根据存储方式分类

队列是逻辑结构,因此以存储方式的不同可以分为顺序队列和链式队列。

顺序队列就是所有的队列元素都存储在连续的地址空间中,因此可以通过数组来实现顺序队列,因为数组的特性也导致顺序队列容量是固定的,不易扩容,这也导致容易浪费空间,同时还要注意元素溢出等问题。

链式队列顾名思义就是采用链式方式存储,可以通过链表实现,因此链式队列可以做到无限扩容,大大的提高了内存利用率。

在这里插入图片描述

2、根据功能特性分类

根据功能特性可以分类出很多专有队列,下面我们列举几个简单介绍一下。

阻塞队列:当空队列时会阻塞出队操作,当满队列时会阻塞入队操作。

优先队列:队列中每个元素都有一个优先级别属性,而元素的出队操作取决于这个优先级别属性,即优先级别高则优先出队。

延迟队列:队列中每个元素都标记一个时间记录,元素只有在指定的延时时间后才会触发出队操作。

循环队列:当使用数组实现队列时,可以通过把队头和队尾相连接,即当队尾到达数组的尾端可以“绕回”数组的开头,通过这种巧妙的设计来提高数组空间利用率。

双端队列:是一种两端都可以进行入队和出队操作的数据结构。

根据这些队列特性,在不同的场景中可以起到意想不到的效果。

下面我们将顺序队列和链式队列的实现进行详细讲解。

03实现(顺序队列)

下面我们借助数组来实现顺序队列,其核心思想是把数组的起始位置作为队头,把数组尾方向作为队尾。当发生出队行为时,需要把剩余所有数据向队头方向移动一位,为什么要做这步操作呢?

首先顺序队列内部是数组,假设数组内可以存放7个元素,此时数组已存满,因此不可以再进行添加新元素入队操作了,然后我们对数组头元素进行出队操作,此时数组因为出队会留下一个空位,如下图。

在这里插入图片描述

那么此时是否可以进行入队操作呢?直接告诉我们应该可以,因为数组头已经有空位了。但是我们约定了队列只能从数组尾进行入队操作,而此时数组尾并没有空位提供给新入队的元素,因此实际上无法进行入队操作。

那要如何处理呢?最简单的方法就是当发生出队操作时,后面所有的元素都向着队头方向移动一位,把队尾空出一位,这每出一个元素就可以入一个元素。

在这里插入图片描述

当然这也不是唯一方案,还是通过循环队列解决这一问题,有兴趣的可以研究一下。

1、ADT定义

我们首先来定义顺序队列的ADT。

ADT Queue{

数据对象:D 是一个非空的元素集合,D = {a1, a2, ..., an},其中 ai 表示队列中的第i个元素,n是队列的长度。数据关系:D中的元素通过它们的索引(位置)进行组织,索引是从0到n-1的整数,并且遵循元素先进先出的原则。基本操作:[Init(n) :初始化一个指定容量的空队列。Capacity:返回队列容量。Length:返回队列长度。Head:返回队头元素,当为空队列则报异常。Tail:返回队尾元素,当为空队列则报异常。IsEmpty():返回是否为空队列。IsFull():返回是否为满队列。Enqueue():入队即添加元素,当为满队列则报异常。Dequeue():出队即返回队头元素并把其从队列中移除,当为空队列则报异常。
]

}

定义好队列ADT,下面我们就可以开始自己实现的队列。

2、初始化 Init

首先定义3个变量用于存放队列元素数组、队列容量以及队尾索引,而没有定义队头索引是因为队头索引永远等于0。

初始化结构主要做几件事。

  • 初始化队列的容量;

  • 初始化存放队列元素数组;

  • 初始化队尾索引;

具体实现代码如下:

//存放队列元素
private T[] _array;
//队列容量
private int _capacity;
//队尾索引,为-1表示空队列
private int _tail;
//初始化队列为指定容量
public MyselfQueueArray<T> Init(int capacity)
{//初始化队列容量为capacity_capacity = capacity;//初始化指定长度数组用于存放队列元素_array = new T[_capacity];_tail = -1;//返回队列return this;
}

3、获取队列容量 Capacity

这个比较简单直接把队列容量私有字段返回即可。

//队列容量
public int Capacity
{get{return _capacity;}
}

4、获取队列长度 Length

我们并没有定义队列长度的私有字段,因为队尾索引即表示数组最后一个元素索引,即可以代表队列长度,因此只需用队尾索引加1即可得到队列长度,同时需要注意判断队列是否为空,如果为空则报错。

//队列长度
public int Length
{get{if (IsEmpty()){return 0;}//队列长度等于队尾索引加1return _tail + 1;}
}

5、获取队头元素 Head

基于我们上面的约定,队头元素永远对应数组的第一个元素,因此可以直接获取索引为0的数组元素。空队列则报错。具体代码如下:

//获取队头元素
public T Head
{get{if (IsEmpty()){//空队列,不可以进行获取队头元素操作throw new InvalidOperationException("空队列");}return _array[0];}
}

6、获取队尾元素 Tail

因为我们定义了队尾索引私有变量,因此可以直接通过队尾索引获取。具体代码如下:

//获取队尾元素
public T Tail
{get{if (IsEmpty()){//空队列,不可以进行获取队头元素操作throw new InvalidOperationException("空队列");}return _array[_tail];}
}

7、获取是否空队列 IsEmpty

是否空队列只需判断队尾索引是否小于0即可。

//是否空队列public bool IsEmpty(){//队尾索引小于0表示空队列return _tail < 0;}

8、获取是否满队列 IsFull

是否满队列只需判断队尾索引是否与队列容量减1相等,代码如下:

//是否满队列
public bool IsFull()
{//队头索引等于容量大小减1表示满队列return _tail == _capacity - 1;
}

9、入队 Enqueue

入队只需向队列内部数组尾添加一个新元素即可,因此先把队尾索引先后移动一位,然后再把新元素赋值给队尾元素,同时还需要检查是否为满队列,如果是满队列则报错,具体实现代码如下:

//入队
public void Enqueue(T value)
{if (IsFull()){//满队列,不可以进行入队列操作throw new InvalidOperationException("满队列");}//队尾索引向后移动1位_tail++;//给队尾元素赋值新值_array[_tail] = value;
}

10、出队 Dequeue

出队则大致分为以下几步:

  • 判断是否为空队列,空队列则报错;

  • 取出队头元素暂存,重置队头元素为默认值;

  • 把队头后面所有元素向队头方向移动一位;

  • 重置队尾元素为默认值;

  • 队尾索引向队头方向移动一位,即队尾索引减1;

  • 返回暂存的队头元素;

具体实现代码如下:

//出队
public T Dequeue()
{if (IsEmpty()){//空队列,不可以进行出队列操作throw new InvalidOperationException("空队列");}//取出队头元素var value = _array[0];//对头元素重置为默认值_array[0] = default;//队头元素后面所有元素都向队头移动一位for (int i = 0; i < _tail; i++){_array[i] = _array[i + 1];}//队尾元素重置为默认值_array[_tail] = default;//队尾索引向队头方向移动一位_tail--;//返回队头元素return value;
}

04实现(链式队列)

我们借助链表来实现链式队列,其核心思想是把链表尾节点作为队尾,把链表首元节点作为队头。

1、ADT定义

相对于顺序队列的ADT来说,链式队列的ADT少了两个方法即获取队列容量和是否满队列,这也是链表特性带来的好处。

2、初始化 Init

首先需要定义链表节点类,包含两个属性数据域和指针域。

然后需要定义3个变量用于存放队头节点、队尾节点以及队列长度。

而初始化结构主要初始化3个变量初始值,具体实现如下:

public class MyselfQueueNode<T>
{//数据域public T Data;//指针域,即下一个节点public MyselfQueueNode<T> Next;public MyselfQueueNode(T data){Data = data;Next = null;}
}
public class MyselfQueueLinkedList<T>
{//队头节点即首元节点private MyselfQueueNode<T> _head;//队尾节点即尾节点private MyselfQueueNode<T> _tail;//队列长度private int _length;//初始化队列public MyselfQueueLinkedList<T> Init(){//初始化队头节点为空_head = null;//初始化队尾节点为空_tail = null;//初始化队列长度为0_length = 0;//返回队列return this;}
}

3、获取队列长度 Length

这个比较简单直接把队列长度私有字段返回即可。

//队列长度
public int Length
{get{return _length;}
}

4、获取队头元素 Head

获取队头元素可以通过队头节点数据域直接返回,但是要注意判断队列是否为空队列,如果为空队列则报异常。具体代码如下:

//获取队头元素
public T Head
{get{if (IsEmpty()){//空队列,不可以进行获取队头元素操作throw new InvalidOperationException("空队列");}//返回队头节点数据域return _head.Data;}
}

5、获取队尾元素 Tail

获取队尾元素可以通过队尾节点数据域直接返回,但是要注意空栈则报异常。具体代码如下:

//获取队尾元素
public T Tail
{get{if (IsEmpty()){//空队列,不可以进行获取队尾元素操作throw new InvalidOperationException("空队列");}//返回队尾节点数据域return _tail.Data;}
}

6、获取是否空队列 IsEmpty

是否空队列只需判断队头节点和队尾节点是否都为空即可。

//是否空队列
public bool IsEmpty()
{//队头节点为null和队尾节点都为空表示空队列return _head == null && _tail == null;
}

7、入队 Enqueue

入队大致分为以下几步:

  • 需要先创建一个新节点;

  • 如果原队尾节点不为空,则把原队尾节点指针域指向新节点;

  • 把原队尾节点更新为新节点;

  • 如果队头节点为空,则说明这是第一个元素,所以队头和队尾都是同一个节点,因此要把队尾节点赋值给队头节点;

  • 队列长度加1;

具体实现代码如下:

//入队
public void Enqueue(T value)
{//创建新的队尾节点var node = new MyselfQueueNode<T>(value);//如果队尾节点不为空,则把新的队尾节点连接到尾节点后面if (_tail != null){_tail.Next = node;}//队尾节点变更为新的队尾节点_tail = node;//如果队头节点为空,则为其赋值为队尾节点if (_head == null){_head = _tail;}//队列长度加1_length++;
}

8、出队 Dequeue

出队则大致分为以下几步:

  • 判断是否空队列,空队列则报错;

  • 获取队头节点数据域暂存;

  • 更新头节点为原队头节点对应的下一个节点;

  • 如果队头节点为空,则说明为空队列,队尾节点也要置空;

  • 队列长度减1;

  • 返回暂存的队头节点数据;

具体实现代码如下:

//出队
public T Dequeue()
{if (IsEmpty()){//空队列,不可以进行出队列操作throw new InvalidOperationException("空队列");}//获取队头节点数据var data = _head.Data;//把队头节点变更为原队头节点对应的下一个节点_head = _head.Next;//如果队列为空,表明为空队列,同时更新队尾为空if (_head == null){_tail = null; }//队列长度减1_length--;//返回队头节点数据return data;
}

:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner

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

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

相关文章

R语言机器学习算法实战系列(八)逻辑回归算法 (logistic regression)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍教程下载数据加载R包导入数据数据预处理数据描述数据切割构建模型预测测试数据评估模型模型准确性混淆矩阵模型评估指标ROC CurvePRC Curve特征的重要性保存模型总结系统信息介绍 …

MySQL数据库和表的基本操作

文章目录 一、数据库的基础知识 背景知识数据库的基本操作二、数据类型 字符串类型数值类型日期类型三、表的基本操作 创建表查看表结构查看所有表删除表 一、数据库的基础知识 背景知识 MySQL是一个客户端服务器结构的程序 主动发送数据的这一方&#xff0c;客户端(client…

“智改数转”转了什么?

万界星空科技专门针对数字化改造申报的MES系统具有显著的技术优势和实施效果&#xff0c;能够为制造型企业提供全方位、高效、可靠的数字化转型支持。项目合作可以私信或者百度上海万界星空科技官网。 “智改数转”是一个综合性的过程&#xff0c;涉及企业多个方面的转型和升…

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

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

【Spring声明式事务失效的12种场景测试】

文章目录 一.Spring声明式事务是什么&#xff1f;二.Spring事务失效的12种场景1.访问权限问题 小结 一.Spring声明式事务是什么&#xff1f; Spring声明式事务是一种通过配置的方式管理事务的方法&#xff0c;它通过注解或XML配置来声明哪些方法需要事务管理&#xff0c;从而将…

JRT怎么从IRIS切换到PostGreSql库

1.执行M导出得到建库脚本文件 2.下载生成的脚本到本地D盘 3.修改驱动为PostGreSql 4.修改连接串 5.到PostGreSql里面创建一个jrtlis的数据库&#xff0c;模式为jrt 6.启动网站点击导入脚本按钮 导入完成了就可以正常使用PostGreSql库了

OpenCV高级图形用户界面(14)交互式地选择一个或多个感兴趣区域函数selectROIs()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 允许用户在给定的图像上选择多个 ROI。 该函数创建一个窗口&#xff0c;并允许用户使用鼠标来选择多个 ROI。控制方式&#xff1a;使用空格键或…

Google FabricDiffusion:开启3D虚拟试穿新篇章

随着数字化转型的步伐不断加快,时尚界也在探索如何利用最新技术为消费者带来更加沉浸式的购物体验。在这一背景下,Google 推出了一项名为 FabricDiffusion 的新技术,这项技术能够将2D服装图像中的高质量织物纹理转移到任意形状的3D服装模型上,从而为3D虚拟试穿提供了更为真…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《基于AGCN-LSTM模型的海上风电场功率概率预测 》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

端到端自动驾驶模型SparseDrive部署过程

SparseDrive 论文链接 https://arxiv.org/pdf/2405.19620 仓库链接 https://github.com/swc-17/SparseDrive 论文和模型的相关介绍大家可以参考其他博客的介绍&#xff0c;这里只介绍模型部署的过程和中间可能遇到的问题解决办法&#xff0c;以及代码解析和使用记录。 模型部署…

CyberRT通信介绍与基于Reader、Writer的通信实践(apollo9.0)

目录 数据通信场景 CyberRT中的通信方式 ​编辑 通信模式 话题通信 服务通信 参数通信 protobuf protobuf简介 protobuf文件编写 topic通信实验 实验环境 实验准备 代码编写 定义消息格式 发送消息 接收消息 定义编译规则 程序编译 运行程序 数据通信场景 …

fabric-sdk-go

Fabric-SDK-go 区块链网络搭建fabric-sdk代码代码结构&#xff1a;代码eg&#xff1a; 区块链网络搭建 使用fabric-sample的网络结构用容器搭建起测试网络即可。 fabric-sdk代码 代码很简易&#xff0c;主要为了了解怎么使用fabric为编程人员提供的sdk从而提供HTTP接口的情况…

浅谈华为 HarmonyOS Next

1. 万物互联时代的新机遇 随着万物互联时代的到来&#xff0c;智能应用从几十亿部手机扩展到数百亿个IoT设备&#xff0c;深刻改变了人们的生活方式。这为我们应用开发者带来了新的机遇和挑战。 机遇 : 目前正处于万物互联时代的前夕&#xff0c;正在经历手机单设备到全场景多…

技术分享:A-23OH型树脂在汽车涂装废溶剂回收中的应用

在当今汽车制造业竞争激烈的环境下&#xff0c;提高生产效率、降低成本的同时&#xff0c;满足环保要求已成为各制造商追求的核心目标。水性涂料因其环保、节能等多重优势&#xff0c;在汽车涂装领域的应用日益广泛。然而&#xff0c;随之而来的喷涂废溶剂处理问题也日益凸显。…

从 Hadoop 迁移到数据 Lakehouse 的架构师指南

从 Hadoop 到数据湖仓一体架构的演变代表了数据基础架构的重大飞跃。虽然 Hadoop 曾经以其强大的批处理能力统治着大数据领域&#xff0c;但如今的组织正在寻求更敏捷、更具成本效益和现代化的解决方案。尤其是当他们越来越多地开始实施 AI 计划时。根本没有办法让 Hadoop 为 A…

三周精通FastAPI:1 第一步入门

FastAPI是一个非常棒的python web和api框架&#xff0c;准备用三周的时间“精通它” 学习流程参考FastAPI官网的用户教程&#xff1a;教程 - 用户指南 - FastAPI 学前提示 运行代码 所有代码片段都可以复制后直接使用&#xff08;它们实际上是经过测试的 Python 文件&#x…

UG NX12.0建模入门笔记:1.0 UG NX12.0安装教程

一、如何关闭防火墙&#xff1f; 提示&#xff1a;安装软件之前&#xff0c;建议先 关闭防火墙和杀毒软件&#xff01;&#xff01;&#xff01; 文章目录 一、如何关闭防火墙&#xff1f;二、UG NX12.0安装包三、UG NX12.0安装教程1.新建文件夹2.安装JAVA环境3.安装许可证管理…

LeetCode_2235. 两整数相加_java

1、问题 2235. 两整数相加https://leetcode.cn/problems/add-two-integers/ 给你两个整数 num1 和 num2&#xff0c;返回这两个整数的和。 示例 1&#xff1a; 输入&#xff1a;num1 12, num2 5 输出&#xff1a;17 解释&#xff1a;num1 是 12&#xff0c;num2 是 5 &am…

智慧监管:EasyCVR视频汇聚智能分析平台助力有限空间作业实现全天候可视化监控

随着工业化和城市化进程的加快&#xff0c;有限空间作业&#xff08;如地下管道、储罐、隧道等&#xff09;在各类企事业单位中日益频繁。然而&#xff0c;这类作业环境复杂、危险系数高&#xff0c;对作业人员的安全管理和实时监控提出了严峻挑战。为了保障有限空间作业的安全…

021_Thermal_Transient_in_Matlab统一偏微分框架之热传导问题

Matlab求解有限元专题系列 固体热传导方程 固体热传导的方程为&#xff1a; ρ C p ( ∂ T ∂ t u t r a n s ⋅ ∇ T ) ∇ ⋅ ( q q r ) − α T d S d t Q \rho C_p \left( \frac{\partial T}{\partial t} \mathbf{u}_{\mathtt{trans}} \cdot \nabla T \right) \nab…