【数据结构】详解队列

news2024/11/26 4:42:19

现在我们来掌握一下队列!如果有对往期知识有不足地方,可翻阅之前文章哦!

个人主页:小八哥向前冲~-CSDN博客

所属专栏:数据结构【c语言版】_小八哥向前冲~的博客-CSDN博客

栈和队列的实现其实都是对你顺序表和链表的检验,只有一些新的概念罢了!

哈哈!不信就往下看吧!!!

目录

什么是队列?

扩展--循环队列

队列的实现

初始化

队列的插入

队列的判空

队列的删除

队列的尾数据

队列的头数据

队列的销毁

总代码

Queue.h文件

Queue.c文件


什么是队列?

  • 队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)
  • 入队列:进行插入操作的一端称为队尾。
  • 出队列:进行删除操作的一端称为队头。

上图理解:

注意:遵循先进先出的原则!这个原则就是区分栈(后进先出)和队列(先进先出)方法!

我们来看看一个队列需要有的最基本要求:详情见--queue - C++ Reference

了解了队列的基本概念,我们来扩展一下!

扩展--循环队列

生活中队列很常用,而在实际的生活中,有时我们会用到循环队列。

循环队列它也有一些应用场景。如:在操作系统中的生产者消费者模型(这个我们后续提到!)

在这种问题中,环形队列可以使用数组实现,也可以使用循环链表实现

我们图上了解:

空的环形队列

满的环形队列:

注意:为了能区别Q.front=Q.rear为队满还是队空,我们通常认为Q.rear+1=Q.front为满!

ok!了解了队列的基本,我们来巩固一下:

3.循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作 后, front=rear=99 ,则循环队列中的元素个数为( )

A .1

B.2

C.99

D.0或者100

4.以下( )不是队列的基本运算?

A.从队尾插入一个新元素

B.从队列中删除第i个元素

C.判断一个队列是否为空

D.读取队头元素的值

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为( )?(假设 队头不存放数据)

A.(rear - front + N) % N + 1

B.(rear - front + N) % N

C.ear - front) % (N + 1)

D.(rear - front + N) % (N - 1)

答案:3.D  4.B   5.B

参考:

3.我们肯定知道元素个数为空时,front=rear,可当front=rear时,也有元素为满的情况!

5.因为是循环队列,单纯尾指针和头指针相减并不能求出总长。

现在我们来实现一下队列(按照一个队列的基本要求)。

当然,队列像栈一样,都可以写成链表和顺序表的底层!这里主要了解链表!

队列的实现

还是老样子:我们创建Queue.h文件声明各种变量和函数,创建Queue.c文件来将函数实现。

思路:

 我们在实现一个函数时,要先弄清楚参数。

既然底层是链表,那么就得定义节点

typedef int QDataType;
//队列节点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

我们来看看我们的底层逻辑:需要头指针记录头,尾指针记录尾,一个计数变量

既然需要这几个变量时刻记录,不如我们直接定义一个结构体来管理这些参数。

作用:

这里将参数管理起来可以避免插入和删除函数参数二级指针的使用!(下面会有体现)

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

ok! 我们来将这些函数一一实现。

初始化

我们得先将记录的参数初始化一下,以便后续参数的更新!

//队列的初始化
void QInit(Queue* p)
{
	assert(p);
	p->phead = p->ptail = NULL;
	p->size = 0;
}

队列的插入

这里谨记:队列是先进先出,栈是后进先出,不要搞混了!

这里由于记录好了尾指针,我们直接在尾后面插入就行!这样看是不是觉得很方便?避免了二级指针的使用。

在插入之前,需要开辟一个节点,然后开始插入!

//队列的插入
void Qpush(Queue* p, QDataType x)
{
	assert(p);
	QNode* node = (QNode*)malloc(sizeof(QNode));
	if (node == NULL)
	{
		perror("malloc failed!");
		return;
	}
	//初始化节点
	node->next = NULL;
	node->val = x;
	//开始插入
	if (p->phead == NULL)
	{
		p->phead = p->ptail = node;
	}
	else
	{
		p->ptail->next = node;
		p->ptail = node;
	}
}

队列的判空

我们只需要在管理的数据变量中判断计数器的值是否为空就行!这也侧面体现了我们用一个结构体管理变量的好处!

//队列的判空
bool QEmpty(Queue* p)
{
	assert(p);
	return p->size==0;
}

队列的删除

  • 在删除之前,我们要先判断队列是否为空,如果为空的话,不能删除。
  • 因为队列是先进先出原则,既然尾部进数据,那么头部出数据。所以我们删除数据要在头部删除!

而在删除时要讨论队列中一个节点还是多个节点问题。本来不讨论我们也能删除数据,那么是为什么要讨论呢?

当队列中只有一个节点时,头指针等于尾指针一起指向这一个节点,当删除时,头指针移向空,然后将这个节点释放掉,但尾指针仍然指向那个节点,当我们访问尾指针指向的那个值时,程序出现错误!

我们上图理解:

于是我们能这样写代码:

//队列的删除
void Qpop(Queue* p)
{
	assert(p);
	//删除之前不能为空
	assert(!QEmpty(p));
	//讨论队列只有一个节点的情况!
	if (p->phead->next == NULL)
	{
		free(p->phead);
		p->phead = p->ptail = NULL;
	}
	else
	{
		QNode* next = p->phead->next;
		free(p->phead);
		p->phead = next;
	}
	p->size--;
}

队列的尾数据

有了前面的基础,我们访问队列尾数据十分简单,但需要注意的是,判断队列是否为空。

//队列的尾数据
QDataType QBack(Queue* p)
{
	assert(p);
	assert(!QEmpty(p));
	return p->ptail->val;
}

队列的头数据

能十分访问尾数据,访问头数据也是如此,但是别忘了要判断队列是否为空。

//队列的头数据
QDataType QFront(Queue* p)
{
	assert(p);
	assert(!QEmpty(p));
	return p->phead->val;
}

队列的销毁

我们动态开辟了内存节点,那么当我们不用了这个队列时,不要忘了销毁它!

//队列的销毁
void QDestroy(Queue* p)
{
	assert(p);
	QNode* cur = p->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

总代码

Queue.h文件

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int QDataType;
//队列节点
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

//队列相关变量
//先进先出
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;


//队列的初始化
void QInit(Queue* p);
//队列的插入
void Qpush(Queue* p, QDataType x);
//队列的判空
bool QEmpty(Queue* p);
//队列的删除
void Qpop(Queue* p);
//队列的尾数据
QDataType QBack(Queue* p);
//队列的头数据
QDataType QFront(Queue* p);
//队列的销毁
void QDestroy(Queue* p);

Queue.c文件

#include"Queue.h"
//队列的初始化
void QInit(Queue* p)
{
	assert(p);
	p->phead = p->ptail = NULL;
	p->size = 0;
}
//队列的插入
void Qpush(Queue* p, QDataType x)
{
	assert(p);
	QNode* node = (QNode*)malloc(sizeof(QNode));
	if (node == NULL)
	{
		perror("malloc failed!");
		return;
	}
	//初始化节点
	node->next = NULL;
	node->val = x;
	//开始插入
	if (p->phead == NULL)
	{
		p->phead = p->ptail = node;
	}
	else
	{
		p->ptail->next = node;
		p->ptail = node;
	}
}
//队列的判空
bool QEmpty(Queue* p)
{
	assert(p);
	return p->phead == NULL;
}
//队列的删除
void Qpop(Queue* p)
{
	assert(p);
	//删除之前不能为空
	assert(!QEmpty(p));
	//讨论队列只有一个节点的情况!
	if (p->phead->next == NULL)
	{
		free(p->phead);
		p->phead = p->ptail = NULL;
	}
	else
	{
		QNode* next = p->phead->next;
		free(p->phead);
		p->phead = next;
	}
	p->size--;
}
//队列的尾数据
QDataType QBack(Queue* p)
{
	assert(p);
	assert(!QEmpty(p));
	return p->ptail->val;
}
//队列的头数据
QDataType QFront(Queue* p)
{
	assert(p);
	assert(!QEmpty(p));
	return p->phead->val;
}
//队列的销毁
void QDestroy(Queue* p)
{
	assert(p);
	QNode* cur = p->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
}

好了,现在你已经掌握了队列,快去题海感受一下吧!

我们下期见!

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

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

相关文章

PCIE协议-2-事务层规范-MEM/IO/CFG request rules

2.2.7 内存、I/O和配置请求规则 以下规则适用于所有内存、I/O和配置请求。每种类型的请求还有特定的额外规则。 所有内存、I/O和配置请求除了常见的头标字段外&#xff0c;还包括以下字段&#xff1a;requester ID[15:0]和Tag[9:0]&#xff0c;形成事务ID。Last DW BE[3:0] a…

【面试经典题】环形链表

个人主页&#xff1a;一代… 个人专栏&#xff1a;数据结构 在面试中我们经常会遇到有关链表的相关题目&#xff0c;面试官通常会对题目给出拓展 下面我就两个leetcode上的一个双指针的题目为例&#xff0c;并对其进行拓展 题目链接&#xff1a;环形链表 题目描述&#xf…

pytest(二)

1.pytest-html⽣成报告 Pytest-HTML 是⼀个插件&#xff0c;它可以⽣成漂亮且易于阅读的 HTML 测试报告。下⾯是使⽤ pytest-html ⽣成报告的步骤&#xff1a; 1. 安装 pytest-html 插件&#xff1a; pip install pytest-html 2. 运⾏测试并⽣成报告 pytest --htmlr…

Java全局异常处理,@ControllerAdvice异常拦截原理解析【简单易懂】

https://www.bilibili.com/video/BV1sS411c7Mo 文章目录 一、全局异常处理器的类型1-1、实现方式一1-2、实现方式二 二、全局异常拦截点2-1、入口2-2、全局异常拦截器是如何注入到 DispatcherServlet 的 三、ControllerAdvice 如何解析、执行3-1、解析3-2、执行 四、其它4-1、设…

初探 JUC 并发编程:独占锁 ReentrantLock 底层源码解析

本篇是关于 JUC 并发包中独占锁 ReentrantLock 底层源码的解析&#xff0c;在阅读之前需要对 AQS 抽象队列有基本的了解。 文章目录 1.1 类图结构1.2 获取锁1&#xff09;void lock() 方法2&#xff09;void lockInterruptibly() 方法3&#xff09;boolean tryLock() 方法4&am…

jenkins+gitlab+sonar自由风格项目配置

新建项目&基本配置 gitlab侧配置 sonar.projectKeytest_sonar sonar.projectNametest_sonar sonar.projectVersion1.0 sonar.sources. sonar.exclusionssrc/layout/** sonar.sourceEncodingUTF-8 sonar.nodejs.executable/app/nodejs/node-v16.20.2-linux-x64/bin/node配置…

Git详解之六:Git工具

现在&#xff0c;你已经学习了管理或者维护 Git 仓库&#xff0c;实现代码控制所需的大多数日常命令和工作流程。你已经完成了跟踪和提交文件的基本任务&#xff0c;并且发挥了暂存区和轻量级的特性分支及合并的威力。 接下来你将领略到一些 Git 可以实现的非常强大的功能&…

Web APIs(获取元素+操作元素+节点操作)

目录 1.API 和 Web API 2.DOM导读 DOM树 3.获取元素 getElementById获取元素 getElementsByTagName获取元素 H5新增方法获取 获取特殊元素 4.事件基础 执行事件 操作元素 修改表单属性 修改样式属性 使用className修改样式属性 获取属性的值 设置属性的值 移除…

视频模糊变清晰,这13个工具总有一个能帮到你,收藏好

1、Topaz Video Enhance AI 这是一款非常专业的视频分辨率放大软件&#xff0c;使用来自多个帧的信息来实现视频升级、去噪、去隔行扫描和恢复的结果。 Topaz Video Enhance AI可以将视频放大和增强8K分辨率的镜头&#xff0c;并提供真实的细节和动作一致性。它采用AI技术实现…

数据库面试总结

数据库相关 mysql使用的函数 字符相关: concant() 连接字符 trim()去除字符的首尾空格 space(n) 返回n个空格 char_length() 返回字符的个数 ucase()/upper()将字符串 s 的所有字母变成大写字母 lcase()/lower() 将字符串 s 的所有字母变成小写字母 substr/substring/mid(s, …

prophet时间序列模型水质预测应用

前言 此前已经分析了&#xff0c;ARIMA 模型在水质预测中的应用&#xff0c;今天用 prophet 模型测试下在水质预测中的效果。 Prophet 简介 Prophet 是 Facebook 于2017年开源的一个时间序列预测框架&#xff0c;特别适合于处理具有明显趋势性和季节性的数据。该模型设计初衷…

AI算法-高数5.2-线性代数-向量间的线性相关、无关定义和结论

宋浩老师课程&#xff1a;3.2 向量间的线性关系&#xff08;二&#xff09;_哔哩哔哩_bilibili 线性相关、不相关结论&#xff1a; 判断线性有关\无关&#xff0c;转化成方程组&#xff1a; 判断条件> 向量线性相关、无关的本质是&#xff1a;除0外能不能找到非0的数据。

【吊打面试官系列】Java高并发篇 - 如何创建守护线程?

大家好&#xff0c;我是锋哥。今天分享关于 【如何创建守护线程&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 如何创建守护线程&#xff1f; 使用 Thread 类的 setDaemon(true)方法可以将线程设置为守护线程&#xff0c;需要注意的是&#xff0c;需要在调用 …

代码缺陷扫描神器——FindBugs

FindBugs目前&#xff0c;主要有三种形式使用&#xff0c;GUI形式、插件形式、Ant脚本形式&#xff0c;在这里只讲述FindBugs作为插件&#xff0c;在Android Studio中的应用。 目录 一、FindBugs基础知识 二、FindBugs使用进阶 网络安全学习路线 &#xff08;2024最新整理&am…

nginx目录枚举修复手册

nginx目录枚举修复手册 漏洞背景 修复方式: ssh zujian2 sudo vi /data/apps/nginx/conf/conf.d/default.conf server {

软件测试,功能测试转测开容易吗?

一、从这个问题&#xff0c;我能读出一些信息如下&#xff1a; 1、不知道您从事测试工作多久了&#xff0c;可以看出您特别羡慕测试开发工程师&#xff1b; 2、 您可能一直从事功能测试工作&#xff0c;工作模式或大环境下&#xff0c;被中了草&#xff0c;想学习测试开发相关…

动手学深度学习18 预测房价竞赛总结

动手学深度学习18 预测房价竞赛总结 李沐老师代码AutoGluonh2o集成学习automlQA 视频&#xff1a; https://www.bilibili.com/video/BV15Q4y1o7vc/?vd_sourceeb04c9a33e87ceba9c9a2e5f09752ef8 代码&#xff1a; https://www.bilibili.com/video/BV1rh411m7Hb/?vd_sourceeb04…

[C++核心编程-09]----C++类和对象之继承

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

python内置函数exec()和eval()区别

在Python中&#xff0c;eval() 和 exec() 都是内置函数&#xff0c;用于执行存储在字符串或对象中的Python代码&#xff0c;但它们之间也有一些区别。 eval() 语法&#xff1a;eval(expression, globalsNone, localsNone) expression&#xff1a;需要求值的字符串表达式。可…

【C++】 string类:应用与实践

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…