数据结构——队列(Queue)

news2024/10/7 6:43:47

目录

1.队列的介绍

2.队列工程

2.1 队列的定义

2.1.1 数组实现队列

2.1.2 单链表实现队列

2.2 队列的函数接口

2.2.1 队列的初始化

2.2.2 队列的数据插入(入队)

2.2.3 队列的数据删除(出队)

2.2.4 取队头数据

2.2.5 取队尾数据

2.2.6 判断队列是否为空

2.2.7 队列长度统计

2.2.8 队列的销毁

 3.队列总结反思


1.队列的介绍

        队列,顾名思义,作为有素质的新时代公民,在现实生活中我们常常会遇到排队的场景,而队列就是借此种情形衍生出来的数据结构形式。在需要排队的时候,我们面对一个队列会自觉地站在队尾。随着当队伍中的人慢慢出队,我们的位置也从队尾慢慢移动到了队头,当我们成为一个队的第一个人时,我们就明白终于轮到我们出队了。队列这种数据结构组织数据的形式和排队的场景十分相似,均为先进先出,后进后出的规则。

        在掌握了栈这种数据结构的基础之上,我们再来学习队列会比较轻松。栈主要实现的是“先进后出,后进先出”的规则,而队列遵循的则是“先进先出,后进后出”的规律。因此我们可以相互类比地进行学习。

2.队列工程

2.1 队列的定义

        在创建队列之前,我们需要考虑队列用什么方式实现。我们依然从数组和链表两种结构去考虑优劣,我们发现队列在出队和访问时需要访问队头,在入队时需要找到队尾,所以我们根据这一特征仔细分析一下队列最合适的实现方式。

2.1.1 数组实现队列

        当我们打算用数组实现队列的时候:

        如果以下标小的一端为队头,我们发现在入队时,我们很容易可以在队尾插入数据。但是在出队的时候,类似于顺序标的头删操作,所有数据前移一位,需要O(n)的时间复杂度。

        如果以下标大的一端为队头,在出队是很容易。但是在入队时同样需要依次挪动数据,也导致了O(n)的时间复杂度。

2.1.2 单链表实现队列

        当使用单链表的时候,头删头插数据很容易,但是尾插尾删因为需要遍历链表找尾而变得复杂。这是我们只需要再引入一个指针指向单链表的尾即可解决这个问题。因为将单链表用作队列的时候,队列只会对队头和队尾进行操作,所以无论哪一边为队头,队列实际操作的都只有链表的头结点和尾结点,所以我们只需要定义指针指向头和尾即可避免遍历的冗余操作。

        弄明白这一点后,我们再来详细讨论一下到底以单链表的哪一边为队头,哪一边为队尾。

        如果以单链表头结点为队头,以尾结点为队尾。在入队的时候我们需要将数据插入队尾,也即在尾结点后插入数据,因为我们提前存储了尾结点位置,所以可以直接将新结点链接在尾结点后。在出队的时候,就相当于删除队头的结点,也就是单链表头删,也没有问题。看来这种方案是个不错的选择。

        如果以单链表头结点为队尾,以尾结点为队头。那么在入队的时候,我们需要将数据插入队尾,也就是单链表头插,我们可以做到直接链接。在出队的时候,我们需要删除队头的数据,即单链表尾删,熟悉单链表的小伙伴们都知道,单链表尾删是需要尾结点前一个结点的(需要改变倒数第二个结点的next,使其为空指针),所以在选取这种方式时,我们使用指针指示的就不该是尾结点了,而应该是尾结点的前一个结点。

        在这篇博客中,我们采取第一种方式(单链表头结点为队头,以尾结点为队尾)。

typedef int QDataType;

typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

        首先定义出单链表的结点结构体,然后再定义出队列的结构体,队列结构体之中看似有三个成员,实际上都是对队列载体——单链表的信息描述,分别是链表头结点(队头),链表尾结点(队尾),链表节点个数(队列长度)。

2.2 队列的函数接口

2.2.1 队列的初始化

        新建了一个队列后,我们首先需要对其进行初始化,将队列结构体中队头、队尾指针置空,将size置为0。

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

2.2.2 队列的数据插入(入队)

        通过我们刚才的分析,入队可以简单的视为尾插,所以我们按照尾插的逻辑来写入队函数即可。

        首先需要开辟空间创建新结点,然后熟悉单链表的小伙伴又知道了,在我们链接结点之前需要对特殊情况进行特殊处理。一般而言,单链表的特殊情况就是空链表和只有一个结点的链表。当链表为空时,队列中phead和ptail指针均为空指针,直接链接肯定会出错,所以当为空链表时,需要特殊处理一下。当链表仅有一个结点时,其ptail就是尾结点,所以不需要特殊考虑,和其余情况一样,可以直接链接。

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}

	newnode->next = NULL;
	newnode->val = x;

	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

2.2.3 队列的数据删除(出队)

        出队操作在这里就相当于头删。对于删除操作我们要做最基本的判断排除空链表的情况,这里我使用了assert断言。然后考虑特殊情况,当链表只有一个结点时,头删后链表为空,队头指针和队尾指针都需要置空,其余情况都是只需要改变队头指针即可。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	QNode* del = pq->phead;
	if (pq->phead == pq->ptail)
	{
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		pq->phead = pq->phead->next;
	}
	free(del);
	del = NULL;

	pq->size--;
}

2.2.4 取队头数据

        很简单的操作,取出队头指针所指结点的保存的值即可。

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

2.2.5 取队尾数据

        取队尾数据在某些场景下会被使用,其方法和取队头数据一样。

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}

2.2.6 判断队列是否为空

        队列为空的特征很多,包括队头指针、队尾指针为空,队列长度为0,任取一个作为判断依据即可。

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->phead == NULL;
}

2.2.7 队列长度统计

        队列的长度由成员size指出,将其作为返回值即可。

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

2.2.8 队列的销毁

        队列实际上是一个链表+一个记录链表信息的结构体,所以在销毁链表的时候,我们需要按照销毁单链表的方式先释放单链表所占用的空间,然后将记录信息的结构体其中的值置0、指针置空,防止野指针。

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* tmp = cur;
		cur = cur->next;
		free(tmp);
		tmp = NULL;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

 3.队列总结反思

        栈和队列都是比较简单的数据结构,分别采取数组和链表实现了“先进后出,后进先出和“先进先出,后进后出”的功能。只要能熟练的控制应用单链表,我觉得队列应该不在话下。队列在具体实际中的用途也很广泛,在广度优先搜索中队列会作为数据存储方式,对所有出现的情况进行记录与拓展。

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

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

相关文章

基于JAVA的教学过程管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 教师端2.2 学生端2.3 微信小程序端2.3.1 教师功能如下2.3.2 学生功能如下 三、系统展示 四、核心代码4.1 查询签到4.2 签到4.3 查询任务4.4 查询课程4.5 生成课程成绩 六、免责说明 一、摘要 1.1 项目介绍 基于JAVAVu…

56、Flink 的Data Source 原理介绍

Flink 系列文章 一、Flink 专栏 Flink 专栏系统介绍某一知识点,并辅以具体的示例进行说明。 1、Flink 部署系列 本部分介绍Flink的部署、配置相关基础内容。 2、Flink基础系列 本部分介绍Flink 的基础部分,比如术语、架构、编程模型、编程指南、基本的…

SpringBoot Import提示Cannot resolve symbol

背景 项目开发过程中经常在IDEA中出现Cannot resolve symbol,但是依赖确定已经通过maven或者gradle依赖了 常见原因 IDEA 存在缓存 File -> Invalidate Caches/Restart jar包的scope不正常,如果只是runtime则无法import,需要调整为com…

LeetCode 2221. 数组的三角和

文章目录 1. 题目 2. 解题 1. 题目 给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是 0 到 9 之间(两者都包含)的一个数字。 nums 的 三角和 是执行以下操作以后最后剩下元素的值: num…

一天一个设计模式---工厂方法

概念 工厂模式是一种创建型设计模式,其主要目标是提供一个统一的接口来创建对象,而不必指定其具体类。工厂模式将对象的实例化过程抽象出来,使得客户端代码不需要知道实际创建的具体类,只需通过工厂接口或方法来获取所需的对象。…

探讨JS混淆技术及其加密解密实例

引言 在当前计算机科学领域中,保护软件代码的安全性和隐私性变得愈发重要。为了防止黑客攻击和恶意软件分析,开发人员采用各种技术来混淆和加密其代码,其中包括JS混淆技术。本文将介绍JS混淆技术的原理和应用,并提供一些相关的加密…

印章管理详解|契约锁帮助提前预防99%的印章风险

传统实体印章不仅存在私刻私盖、盗用乱用、易伪造等安全隐患,此外,线下面签的方式也不便于异地、非工作时间用印,分公司用印常常两地来回跑。组织的印章到底怎么“管”才能保障安全和使用效率? 一、 印章管理风险有哪些&#xff…

Python爬虫-爬取豆瓣Top250电影信息

🎈 博主:一只程序猿子 🎈 博客主页:一只程序猿子 博客主页 🎈 个人介绍:爱好(bushi)编程! 🎈 创作不易:喜欢的话麻烦您点个👍和⭐! 🎈…

物理环境测评

1.1 物理位置选择 1.1.1 防震防风防雨 安全要求 机房场地选择在具有防震防风防雨等能力的建筑内 测评方法 核查是否有建筑物抗震设防审批文档 核查是否有雨水渗透的痕迹 核查是否有可灵活开启的窗户,若有窗户,是否做了封闭,上锁等防护措…

lua每日tips

目录 1,EC618系列不支持win7下刷机2,为何室内无法gps定位3,Lot平台自动锁定4, LuaTools的部分操作界面是支持拖拽功能的5,Air780E省略 1,EC618系列不支持win7下刷机 1, EC618系列不支持win7下刷…

数据结构-怀化学院期末题(322)

图的深度优先搜索 题目描述: 图的深度优先搜索类似于树的先根遍历,是树的先根遍历的推广。即从某个结点开始,先访问该结点,然后深度访问该结点的第一棵子树,依次为第二顶子树。如此进行下去,直到所有的结点…

nginx部署前端项目自动化脚本

文章目录 配置入口服务器nginx的conf.d使用docker创建一个nginx配置自动化脚本 前言 将项目 通过nginx 部署到 新的服务器 通过nginx反向代理出去 配置入口服务器nginx的conf.d 一般在这个文件夹下 找不到使用 find / -name nginx 2>/dev/null 找到nginx 的位置如果有些没有…

限制选中指定个数CheckBox控件(2/2)

实例需求:工作表中有8个CheckBox控件(下文中简称为控件),现在需要实现限制用户最多只能勾选4个控件。 在上一篇博客中已经实现了这个需求,其基本思路是用户选中第5个控件时,事件代码将取消勾选最后一个选中…

Python 开源扫雷游戏 PyMine 发布介绍视频

Python 开源扫雷游戏 PyMine 发布介绍视频 Python 开源扫雷游戏 PyMine 是笔者开发的基于 wxPython 的 Python 扫雷游戏,现已发布介绍视频。视频请见:https://www.bilibili.com/video/BV1aW4y1N7Dd/ PyMine 比较忠实的还原了微软的扫雷游戏。在算法设计…

HarmonyOS应用开发者基础认证考试

判断题 1.Ability是系统调度应用的最小单元,是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。 正确(True) 2.所有使用Component修饰的自定义组件都支持onPageShow,onBackPress和onPageHide生命周期函数。 错误(False) 3.每调用一次router.pushUrl()方法,…

【我与Java的成长记】之封装,继承详解(一)(能看懂文字就能明白系列)

系列文章目录 能看懂文字就能明白系列 C语言笔记传送门 Java笔记传送门 🌟 个人主页:古德猫宁- 🌈 信念如阳光,照亮前行的每一步 文章目录 系列文章目录🌈 *信念如阳光,照亮前行的每一步* 前言一、封装&am…

美国NDC药品码注册详细介绍(OTC药品FDA注册)

美国药品验证号(NDC)注册介绍 希望我发布的文章能帮助到刷到的有缘人 国家药品验证号(NDC)是中国中药及OTC非处方药通过FDA认证的快捷申请形式。主要针对美国国家药典已有的药,无需做新药论证,只需提供必…

Mysql :Failed to obtain JDBC Connection

驱动版本需要跟新&#xff0c;同时驱动对应的pom依赖需要更新成mysql驱动对应版本&#xff0c;这里使用8.0.26版本。 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-connector}<…

C++八股学习心得.8

1.const知道吗&#xff1f;解释其作用 const 修饰类的成员变量&#xff0c;表示成员常量&#xff0c;不能被修改。 const修饰函数承诺在本函数内部不会修改类内的数据成员&#xff0c;不会调用其它非 const 成员函数。 如果 const 构成函数重载&#xff0c;const 对象只能调…

你好,2024!再见,2023!

大家好&#xff0c;我是南城余&#xff01; 今天是2023年最后一天&#xff0c;看到各位大佬都在分享今年的总结&#xff0c;我也来做个年度总结&#xff0c;是第一次做年度总结&#xff0c;希望以后可以每年都做一个好好的回顾。 说来可笑&#xff0c;标题&#xff0c;2023怎么…