【数据结构】队列详解

news2025/5/10 1:10:35

前言

前面我们学习了一种数据结构:栈,栈是一种只允许在一端尽进行插入删除的数据结构,而今天我们将学习另一种数据结构:队列,队列是一种支持在一端进行插入,在另一端进行删除的数据结构。

一、队列的介绍

队列是一种支持在一端进行插入,在另一端进行删除的数据结构,相当于尾插和头删,入队列的一端我们称之为队尾,出队列的一端我们称之为对头。
在这里插入图片描述

  • 入队:向队列插入元素的操作,只能从队尾插入元素
  • 出队:对队列进行删除元素的操作,只能从队头删除元素
  • 基于队列上面的性质,队列的特点是先进先出的。这个需要和栈的后进先出区分开。

二、队列数据类型的重定义

在这里插入图片描述
与前面学习的数据结构一样,为了能够方便修改队列存储的数据类型,我们需要队数据类型进行重定义

三、队列的结构

因为队列中需要进行入队和出队,即尾插和头删,头删数组就不太方便了,因为数组的头删需要挪动后面的数据,效率比较低,所以采用链表的形式来实现队列。为了效率,我们需要准备两个指针来管理整个队列,因为我们需要对这个队列进行头删和尾插,所以需要两个指针分别标识队列的头结点和尾结点。实现链表的形式的队列,那么我们首先需要确定链表的结点的结构

  1. 队列中链表的结点的结构
    在这里插入图片描述
    和普通链表一样,队列中的链表同样需要存储数据,所以需要一个数据域,每一个结点需要找到其下一个结点,因此需要一个指针域指向每一个结点的下一个结点
    为了后面方便表示,我们同样可以对这个结点的结构进行重定义
    在这里插入图片描述
  2. 队列中的头指针和尾指针
    因为这是两个指针,所以我们可以考虑将这两个指针封装称为一个结构体,叫做队列Queue.也就是一个队列只需要知道其头结点和尾结点,那么我们就可以对这个队列进行操作了
    在这里插入图片描述
    同样的道理,为了后续方便表示,我们可以对这个结构进行重定义
    在这里插入图片描述

四、队列常见的基本操作

1. 声明

// 基本操作的声明、

// 初始化
void QueueInit(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

// 入队
void QueuePush(Queue* pq, QDataType val);

// 出队
void QueuePop(Queue* pq);

// 判空
bool QueueEmpty(Queue* pq);

// 队头元素
QDataType QueueFront(Queue* pq);

// 队尾元素
QDataType QueueBack(Queue* pq);

// 队列结点个数
size_t QueueSize(Queue* pq);

在上面的函数声明中,我们发现函数的参数传的是队列结构体的地址,而不是结构体本身,道理和栈中的传参是一样的,首先可以节省空间,其次,我们需要在函数中通过这个队列的结构体指针找到队列的队头指针和队尾指针,如果传的是结构体,那么传参的过程是一次深拷贝,形参是实参的一份临时拷贝,这是两份不同的数据了,通过形参结构体找到的队头指针和队尾指针和实参的队头指针和队尾指针不是同一份数据,因此我们传的是队列的结构体指针。

2. 定义

  • 初始化
// 初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

因为队列中有两个指针,所以我们需要对队列进行初始化,防止队列的两个指针变成野指针,初始化就是将队列的两个指针置成空指针

  • 销毁队列
// 销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

这个思路和销毁链表的思路是一样的,需要遍历队列中的每一个结点,然后依次进行释放结点空间,最终将队列的头指针和尾指针置成空指针即可。

  • 入队
// 入队
void QueuePush(Queue* pq, QDataType val)
{
	assert(pq);
	// 申请新节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		return;
	}
	newnode->val = val;
	newnode->next = NULL;

	// 入队
	if (pq->tail == NULL)
	{
		// 第一次入队
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}

}

入队的步骤,首先申请空间创建新节点,在入队的时候因为这个队列的链表是没有头结点的,所以在入队的时候需要判断是否为第一次入队,如果是第一次入队,则需要同时将头指针和尾指针指向这个结点,即第一个结点,如果不是第一次入队,则只需要将尾指针的下一个更新为新插入的元素(结点),然后更新尾指针即可。

  • 出队
// 出队
void QueuePop(Queue* pq)
{
	assert(pq);
	// 删除操作一定要判断是否为空
	assert(pq->head);
	// 删除结点
	// 先保存下一个位置
	QNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;
}

出队的时候即删除元素的时候一定要检查队列是否为空,如果队列为空,是不能删除元素的,队列的删除元素的方式和链表删除元素的方式是一样的,先保存第二个结点,再删除第一个结点,再更新第二个结点为新的第一个结点。

  • 判空
// 判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

当队列中的头指针为空的时候队列就是空的

  • 返回队头元素
// 队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->val;
}
  • 返回队尾元素
// 队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->tail->val;
}

这两个函数接口需要注意判断队列是否为空,如果队列为空,则队列中没有元素,是不能返回队头元素和队尾元素的

  • 队列元素个数
int QueueSize(Queue* pq)
{
	if (pq->head == NULL)
	{
		return 0;
	}
	int count = 1;
	QNode* cur = pq->head;
	while (cur!=pq->tail)
	{
		cur = cur->next;
		count++;
	}
	return count;
}

注意此时count的初始值,当count初始值为0时,需要从头遍历到空,当count的初始值为1时,只需要遍历到尾结点即可。

五、遍历队列

  1. 代码
void test_queue5()
{
	// 遍历队列
	Queue q;
	// 初始化
	QueueInit(&q);
	// 入队
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	printf("此时队列的队头元素为:%d\n", QueueFront(&q));
	printf("此时队列的队尾元素为:%d\n", QueueBack(&q));
	printf("此时队列元素个数为:%d\n", QueueSize(&q));
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
	printf("此时队列元素个数为:%d\n", QueueSize(&q));
	
}
  1. 结果
    在这里插入图片描述
  2. 思路分析
    遍历队列需要一个循环来实现,当队列不为空时,先访问队头元素,再删除队头元素(入队),知道队列为空时退出循环。

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

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

相关文章

PHP反序列化字符串逃逸

PHP反序列化字符串逃逸 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录PHP反序列化字符串逃逸前言一、关于反序列化和序列化二、[0ctf 2016]unserialize二、prize_p5[NSSCTF]前言 例如:最近日常刷题玩…

常用的传输码介绍

文章目录前导知识1.AMI码2.HDB3码3.PST码4.数字双相码5.CMI码6.nBmB码前导知识 在介绍常用的传输码之前,先简单介绍一下直流分量。 信号的直流分量就是信号的平均值,它是一个与时间无关的常数,直流分量的数学公式表示为: 判断有…

基于轻量级YOLOv5+Transformer的汽车车损检测识别分析系统

将传统NLP领域提出来的Transformer技术与yolo目标检测模型融合已经成为一种经典的做法,早在之前的很多论文里面就有这种组合应用的出现了,本文主要是借鉴前文的思路,开发基于yolov5transformer的汽车车损检测识别模型,首先看下效果…

光流相关总结

基于图像亮度恒定假设, 图像亮度:I(x⃗,t)I(\vec x, t)I(x,t), 其中x⃗[x,y]\vec x[x,y]x[x,y],那么亮度恒定假设: I(x⃗,t)I(x⃗δx⃗,tδt)(1)I(\vec x,t)I(\vec x \delta \vec x, t \delta t) (1)I(x,t)I(xδx,tδt)(1) 对上式…

2022年值得记录的一年,事与愿违的一年

年初带着对生活的不满、怀才不遇的傲慢; 愿即将到来的30岁不留遗憾; 你放下所有去追求向往的样子; 那时所有的空气都是清新的,即使它满是灰尘; 不再年少的你依然充满新奇; 用尽力气把自己钉在那个不属…

前端与后端的技术通性

一、后端的JDK相当于前端的Node.js, 后端的JVM相当于前端的V8引擎【作用示例图,如下所示】 【Nodejs、JDK分别是前后端的运行环境】 二、后端的Maven(基于项目对象模型-Project Object Model-POM的项目管理机制)相当于前端的npm(n…

FlinkCDC

目录1、CDC 简介1.1、什么是CDC1.2、CDC的种类1.3、Flink-CDC2、Flink CDC 网址3、运行原理5、简要安装6、开发案例7、扩展1、CDC 简介 1.1、什么是CDC CDC 是 Change Data Capture(变更数据获取)的简称。核心思想是,监测并捕获数据库的变动…

js实现网页特效

文章目录一、元素偏移量offest系列🥇offset与style的区别🎓案例1🦹🏽‍♂️案例2🐼案例3二、元素可视区client系列三、元素滚动scroll系列🏂🏿案例4:🔭补充 mouseenter事…

大数据分析案例-基于KNN算法对茅台股票进行预测

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

一个精美的主界面窗口功能的设计和实现原来如此简单,万字肝爆

👨‍💻个人主页:元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏 玩归玩闹归闹,别拿java开玩笑 —————————————————— ⭐相关文章⭐ -通过窗口看…

数据结构与算法:栈和队列的学习

1.栈 1.栈的定义 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈&am…

AirServer2023免费无线Mac和PC电脑屏幕镜像投屏工具

AirServer2023是适用于 Mac 和 PC 的先进的屏幕镜像接收器。 它允许您接收 AirPlay 和 Google Cast 流,类似于 Apple TV 或 Chromecast 设备。AirServer 可以将一个简单的大屏幕或投影仪变成一个通用的屏幕镜像接收器 ,是一款十分强大的投屏软件。AirSer…

Ansys Zemax | 眼科镜片设计

本文介绍了眼科镜片的设计原理,并讨论了镜片、眼睛和视觉环境中对镜片设计十分关键的参数,其中包括了常见镜片材料(涵盖了玻璃和聚合物)的玻璃目录。本文不包括渐进式镜片设计,尽管渐进式镜片时常根据一般的镜片曲率原…

【实际开发04】- XxxMapper.xml/java - 批量处理

目录 1. Model : XxxMapper.xml 1. IotTypeMapper.xml 基础 3 tips 2. Model : XxxMapper.java 1. IotTypeMapper.java 基础 3 tips 3. Others info 1. 模糊查询 2. 模糊查询 name 导致的异常 --> name 3. 连接查询 Where 限制主表 , 谨慎 : 使用副表限制 - ★ 4…

Java程序员如何使用代码来计算最大公约数和最小公倍数?

沉淀、分享、成长,让自己和他人都能有所收获!😄 一、前言 嘿,怎么突然讲到最大公约数了? 因为RSA算法,对于与欧拉结果计算的互为质数的公钥e,其实就需要使用到辗转相除法来计算出最大公约数。…

Java文件IO操作

目录 一、了解什么是文件 狭义的文件: 广义的文件: 二、文件的路径 ①文件的绝对路径 ②文件的相对路径 三、Java对于文件的操作 File类的构造方法 File类的普通方法 四、对于文件的内容操作 ①FileInputStream(文件输入流&#xf…

ES索引备份还原

ES索引备份还原一、规划二、备份方案一:备份到集群共享目录方案二:备份到HDFSES还原一、规划 es数据出于线上数据安全考虑,对于es已有的索引数据可以进行安全备份,通常可以将es备份到共享文件目录或者一些其它的数据存储的文件系…

Splashtop Personal 安装教程

splashtop Personal 安装教程1. Splashtop Personal 概述2. splashtop Personal 安装步骤2.1 主控端(Splashtop Business app)2.2 被控端(Splashtop Streamer)2.3 打开主控端结束语1. Splashtop Personal 概述 Splashtop Persona…

java跳出循环的几种方式

在java中可以使用break、continue、return语句跳出for循环。break用于完全结束一个循环,跳出循环体;continue只是中止本次循环,接着开始下一次循环;return的功能是结束一个方法。 break语句 break用于完全结束一个循环&#xff0…

4.5 集成运放的种类及选择

一、集成运放的发展概述 集成运放自 20 世纪 60 年代问世以来,飞速发展,目前已经历了四代产品。 第一代产品基本沿用了分立元件放大电路的设计思想,采用了集成数字电路的制造工艺,利用了少量横向 PNP 管,构成以电流源…