队列的讲解

news2024/12/23 23:20:08

队列的概念

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

一端进另一端出

也就是可以做到,先进先出

队列的使用场景

队列的经典应用就是保持公平性

比如:抽号机

生产者消费者模型,先来先出去,多线程的话就可能涉及到一个锁的概念

广度优先遍历(概念)

直接好友:好友

间接好友:好友的好友

可以采取队列的方式推荐

队列如何实现

数组的无法实现的因为需要一边进一边出,所以一般是单链表实现队列

同时单链表也会更加省空间

队列的实现 

创建文件

首先我们需要知道单链表实现队列的时候,我们需要有头节点和尾结点,以及链表的实际的个数,所以我们不能在结构体里面创建那麽多结构体,所以我们采取结构体的嵌套

创建栈的结构

// 链式结构:表示队列 
typedef struct QListNode
{
	QDataType _data;
	struct QListNode* _next;
}QNode;
// 队列的结构 (因为队列是先进先出,后进后出,也就是和栈是相反的,此时会尾进头出,所以我们需要更新尾部和头部节点)
// (把头结点,尾结点,链表的实际的大小放里面,这样不需要每次使用的时候进行循环找尾,我们只需要每次更新尾结点就可以)
typedef struct Queue
{
	QNode* _phead;//头节点
	QNode* _ptail;//尾结点
	int size;
}Queue;

这段代码定义了两个结构体,QNodeQueue,分别用于表示队列中的节点和整个队列。下面是对每个部分的详细解释:

  1. QNode 结构体:这个结构体表示队列中的单个节点。

    • _data:这是 QNode 结构体中的一个成员变量,用来存储节点中的数据。QDataType 是数据类型的别名,它应该在相关的头文件中定义,代表节点存储的数据类型。
    • _next:这是一个指向 QNode 类型的指针,用来指向同一队列中的下一个节点。如果 _next 为 NULL,表示这是队列中的最后一个节点。
  2. Queue 结构体:这个结构体表示整个队列。

    • _phead:这是一个指向 QNode 类型的指针,用来指向队列的头节点。队列的头节点是最早被加入的节点,也是下一个将要被移除的节点。
    • _ptail:这是一个指向 QNode 类型的指针,用来指向队列的尾节点。队列的尾节点是最后被加入的节点,它用于在添加新元素时更新队列。
    • size:这是一个整数类型的变量,用来存储队列中当前的元素数量。

在队列的链式表示中,不需要像顺序表示(使用数组)那样进行动态内存分配和缩容。链式结构自然地允许队列的动态增长和缩减,因为每个节点独立维护其后继节点的指针。

队列的基本操作包括:

  • 入队(Enqueue):在队尾添加一个新节点。
  • 出队(Dequeue):移除队头的节点,并返回其数据。
  • 查看队头(Peek/Front):返回队头节点的数据但不移除它。
  • 检查队列是否为空(IsEmpty):检查队列的头节点是否为 NULL

初始化与销毁队列

// 初始化队列 
void QueueInit(Queue* ps)
{
	ps->_phead = NULL;
	ps->_ptail = NULL;
	ps->size = 0;
}
// 销毁队列 
void QueueDestroy(Queue* ps)
{
	assert(ps);
	QNode* cur = ps->_phead;
	while (cur)
	{
		//存下下一个节点的地址,不会出现找空的情况
		QNode* next = cur->_next;
		free(cur);
		cur =next;
	}
}
  1. QueueInit 函数

    • 这个函数接收一个指向 Queue 结构体的指针 ps
    • ps->_phead = NULL;:将队列的头节点指针设置为 NULL。这表示队列初始化时是空的,没有任何元素。
    • ps->_ptail = NULL;:将队列的尾节点指针也设置为 NULL。由于队列是空的,头尾节点都不存在。
    • ps->size = 0;:设置队列中元素的数量为0。
  2. QueueDestroy 函数

    • 这个函数同样接收一个指向 Queue 结构体的指针 ps
    • assert(ps);:使用 assert 宏来确保传入的 ps 不是 NULL。如果 ps 是 NULLassert 将触发断言失败,这有助于避免对空指针的无效操作。
    • QNode* cur = ps->_phead;:声明一个 QNode 类型的指针 cur 并初始化为队列的头节点。
    • while (cur):使用 while 循环来遍历队列,直到 cur 为 NULL,即队列中的所有节点都被处理完毕。
    • QNode* next = cur->_next;:在释放当前节点之前,先保存下一个节点的地址,以便在释放当前节点后能够继续遍历队列。
    • free(cur);:使用 free 函数释放当前节点 cur 所占用的内存。
    • cur = next;:将 cur 更新为下一个节点,继续循环直到所有节点都被释放。
    • 循环结束后,队列中的所有节点都被释放,此时队列被销毁。

队尾入队列 

// 队尾入队列 
void QueuePush(Queue* ps, QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("QueuePush:newnode:error:");
		exit(1);
	}
	//把需要插入的数值插入到节点里面
	newnode->_next = NULL;
	newnode->_data = x;
	
	// ps->_phead == ps->_ptail 的结果确实是 true,这表明队列中只有一个元素(头尾相接)。
	// 但是,通常情况下,我们不会使用 ps->_phead == ps->_ptail 
	// 来检查队列中是否只有一个元素。相反,我们通常会使用 ps->size == 1 或者 ps->_phead != NULL && ps->_ptail != NULL 
	// 来检查队列中是否只有一个元素。

	//插入节点 第一个节点,这里的判断是不能写成ps->_phead ==ps->_ptail;因为都是空指针不能进行比较
	//ps->_phead == NULL
	if (ps->size == 0)
	{
		ps->_phead = ps->_ptail = newnode;
	}
	else
	{
		//尾插节点
		ps->_ptail->_next = newnode;
		ps->_ptail= newnode;
	}
	ps->size++;
}

这段代码定义了一个名为 QueuePush 的函数,用于在队列的尾部添加一个新的元素。

  1. void QueuePush(Queue* ps, QDataType x):函数定义开始,QueuePush 接收一个指向 Queue 结构体的指针 ps 和一个要入队的新数据 x

  2. QNode* newnode = (QNode*)malloc(sizeof(QNode));:使用 malloc 函数为新节点分配内存。newnode 是指向新分配内存的指针。

  3. if (newnode == NULL):检查 malloc 是否成功分配了内存。如果 newnodeNULL,表示内存分配失败。

  4. perror("QueuePush:newnode:error:");:如果内存分配失败,使用 perror 函数输出错误信息。

  5. exit(1);:如果内存分配失败,调用 exit 函数以非零状态码退出程序。

  6. newnode->_next = NULL;:将新节点的 _next 指针设置为 NULL。这是新节点的尾部标识,表示在新节点之后没有更多的节点。

  7. newnode->_data = x;:将新数据 x 存储在新节点的 _data 成员中。

  8. if (ps->size == 0):检查队列是否为空(即队列中的元素数量为0)。

  9. ps->_phead = ps->_ptail = newnode;:如果是空队列,那么新节点同时是头节点和尾节点。因此,将头节点和尾节点指针都指向 newnode

  10. else:如果队列不是空的,执行以下操作:

    • ps->_ptail->_next = newnode;:将当前尾节点的 _next 指针指向新节点,从而将新节点添加到队列的末尾。
    • ps->_ptail = newnode;:更新尾节点指针,使其指向新节点。
  11. ps->size++;:队列中元素的数量增加1。

这段代码实现了队列的尾部入队操作。它首先检查队列是否为空,然后相应地将新节点添加到队列的末尾,并更新尾节点指针和队列的元素数量。如果内存分配失败,程序将输出错误信息并退出。

队头出队列 

// 队头出队列 
void QueuePop(Queue* ps)
{
	assert(ps && ps->size);
	// 改变头结点
	ps->_phead = ps->_phead->_next;
	ps->size--;
}

这段代码定义了一个名为 QueuePop 的函数,其目的是从队列头部移除一个元素。

  1. #include"Queue.h":这行代码应该位于文件的顶部,用于引入包含 Queue 结构体和 QNode 结构体定义以及 QDataType 类型定义的头文件。

  2. void QueuePop(Queue* ps):函数定义开始,QueuePop 接收一个指向 Queue 结构体的指针 ps 作为参数。

  3. assert(ps && ps->size);assert 是一个宏,用于在运行时测试一个条件是否为真。如果条件为假,则 assert 将产生一个断言失败,通常会导致程序异常终止。在这里,它用于确保传入的队列指针 ps 不是 NULL,并且队列中至少有一个元素(即 ps->size 大于0)。

  4. ps->_phead = ps->_phead->_next;:这行代码将队列的头节点指针 _phead 更新为指向下一个节点,从而移除当前队列头部的节点。由于队列是先进先出(FIFO)的数据结构,队头节点是将要被移除的节点。

  5. ps->size--;:队列中元素的数量减少1。

这段代码实现了队列的头部出队操作。它首先通过 assert 检查队列是否非空,然后将头节点指针移动到下一个节点,以此出队操作,最后更新队列的元素数量。

获取队列头部元素

// 获取队列头部元素 
QDataType QueueFront(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_phead->_data;
}
  1. QueueFront 函数

    • 这个函数接收一个指向 Queue 结构体的指针 ps
    • assert(ps && ps->size);:使用 assert 宏确保传入的队列指针 ps 不是 NULL,并且队列中至少有一个元素(即 ps->size 大于0)。
    • return ps->_phead->_data;:返回队列头部节点的 _data 成员。由于队列是先进先出(FIFO)的数据结构,队头节点的 _data 成员包含了最早被加入的元素的值。

获取队列队尾元素 

// 获取队列队尾元素 
QDataType QueueBack(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_ptail->_data;
}
  1. QueueBack 函数

    • 这个函数同样接收一个指向 Queue 结构体的指针 ps
    • assert(ps && ps->size);:使用 assert 宏确保传入的队列指针 ps 不是 NULL,并且队列中至少有一个元素。
    • return ps->_ptail->_data;:返回队列尾部节点的 _data 成员。在链式队列中,尾部节点是最后一个被加入的节点,其 _data 成员包含了最近一个被加入的元素的值。

获取队列中有效元素个数

// 获取队列中有效元素个数 
int QueueSize(Queue* ps)
{
	return ps->size;
}
  1. QueueSize 函数

    • 这个函数接收一个指向 Queue 结构体的指针 ps
    • return ps->size;:返回队列中有效元素的个数,即 size 成员的值。

检测队列是否为空,如果为空返回非零结果,如果非空返回0 

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->size == 0;
}

QueueEmpty 函数

  • 这个函数接收一个指向 Queue 结构体的指针 ps
  • assert(ps);:使用 assert 宏确保传入的队列指针 ps 不是 NULL
  • return ps->size == 0;:检查队列是否为空。如果 size 成员的值等于0,则表示队列为空,函数返回非零值(在C语言中,非零值被视为“真”),否则返回0(“假”)

 队列代码的总结 

Queue.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{
	QDataType _data;
	struct QListNode* _next;
}QNode;
// 队列的结构 (因为队列是先进先出,后进后出,也就是和栈是相反的,此时会尾进头出,所以我们需要更新尾部和头部节点)
// (把头结点,尾结点,链表的实际的大小放里面,这样不需要每次使用的时候进行循环找尾,我们只需要每次更新尾结点就可以)
typedef struct Queue
{
	QNode* _phead;//头节点
	QNode* _ptail;//尾结点
	int size;
}Queue;
// 这里采取一级指针进行实现代码逻辑,如果不创建队列的结构,我们就需要采取二级指针
// 初始化队列 
void QueueInit(Queue* ps);
// 销毁队列 
void QueueDestroy(Queue* ps);
// 队尾入队列 
void QueuePush(Queue* ps, QDataType data);
// 队头出队列 
void QueuePop(Queue* ps);
// 获取队列头部元素 
QDataType QueueFront(Queue* ps);
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps);
// 获取队列中有效元素个数 
int QueueSize(Queue* ps);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps);

Queue.c

#include"Queue.h"
// 初始化队列 
void QueueInit(Queue* ps)
{
	ps->_phead = NULL;
	ps->_ptail = NULL;
	ps->size = 0;
}
// 销毁队列 
void QueueDestroy(Queue* ps)
{
	assert(ps);
	QNode* cur = ps->_phead;
	while (cur)
	{
		//存下下一个节点的地址,不会出现找空的情况
		QNode* next = cur->_next;
		free(cur);
		cur =next;
	}
}
// 队尾入队列 
void QueuePush(Queue* ps, QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("QueuePush:newnode:error:");
		exit(1);
	}
	//把需要插入的数值插入到节点里面
	newnode->_next = NULL;
	newnode->_data = x;
	
	// ps->_phead == ps->_ptail 的结果确实是 true,这表明队列中只有一个元素(头尾相接)。
	// 但是,通常情况下,我们不会使用 ps->_phead == ps->_ptail 
	// 来检查队列中是否只有一个元素。相反,我们通常会使用 ps->size == 1 或者 ps->_phead != NULL && ps->_ptail != NULL 
	// 来检查队列中是否只有一个元素。

	//插入节点 第一个节点,这里的判断是不能写成ps->_phead ==ps->_ptail;因为都是空指针不能进行比较
	//ps->_phead == NULL
	if (ps->size == 0)
	{
		ps->_phead = ps->_ptail = newnode;
	}
	else
	{
		//尾插节点
		ps->_ptail->_next = newnode;
		ps->_ptail= newnode;
	}
	ps->size++;
}
// 队头出队列 
void QueuePop(Queue* ps)
{
	assert(ps && ps->size);
	// 改变头结点
	ps->_phead = ps->_phead->_next;
	ps->size--;
}
// 获取队列头部元素 
QDataType QueueFront(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_phead->_data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps)
{
	assert(ps && ps->size);
	return ps->_ptail->_data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* ps)
{
	return ps->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps)
{
	assert(ps);
	return ps->size == 0;
}

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

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

相关文章

数据结构与算法-排序算法3-插入排序

目录 1.插入排序&#xff1a; 1.介绍&#xff1a; 2.动态图解 3.举例 4.小结插入排序规则 5.插入排序代码 6.运行时间 代码&#xff1a; 运行结果&#xff1a; 1.插入排序&#xff1a; 1.介绍&#xff1a; 数组中n个元素&#xff0c;把这n个待排序元素看成一个有序序…

01 Triton backend

1 整体架构 三部分组成&#xff1a; Triton backend tensorRT_backend、onnx_backend、tfs_backend、torch_backend **Triton model ** 不同的模型 **Triton model instance ** 模型实例 ![P2}5X%2ULV(2OAC$_OKOP.png 2 设计思路 需要实现七个接口&#xff1a; TRITON…

Elasticsearch - HTTP

文章目录 安装基本语法索引创建索引查看索引删除索引 文档创建文档更新文档匹配查询多条件查询聚合查询映射 安装 https://www.elastic.co/downloads/past-releases/elasticsearch-7-17-0 下载完成启动bin/elasticsearch服务&#xff0c;可以在Postman调试各种请求。 基本语法…

风电功率预测 | 基于遗传算法优化BP神经网络实现风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测 | 基于遗传算法优化BP神经网络实现风电功率预测(附matlab完整源码)完整代码风电功率预测 | 基于遗传算法优化BP神经网络实现风电功率预测(附matlab完整源码) 基于遗传算法优化BP神经网络是一种常见的方法,用于改进BP神经网络在风电功率预测中的性…

IBM Granite模型开源:推动软件开发领域的革新浪潮

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

基于MSWA相继加权平均的交通流量分配算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于MSWA相继加权平均的交通流量分配算法matlab仿真.如图所示交通网络中&#xff0c;包含6个节点、11各路段、9个OD对。经枚举可得每个OD对间存在3条无折返有效路…

RS编码和卷积码总结

RS编码 简要介绍RS编码及其原理 1. RS编码简介 Reed-Solomon编码&#xff08;RS编码&#xff09;是一种强大的纠错码&#xff0c;广泛应用于数据存储和传输中。RS编码由Irving S. Reed和Gustave Solomon于1960年提出&#xff0c;属于BCH码的一种&#xff0c;是基于有限域&am…

免费思维13招之十二:耗材型思维

免费思维13招之十二:耗材型思维 今天给你分享免费思维的两个子思维——相关性耗材思维和非相关性耗材思维。 相关性耗材思维,是指有一些产品的使用,需要大量的相关耗材,从而对该产品进行免费,而耗材进行资费。 举例:全世界最大的一家直销公司叫安利,它的明星产品是净水…

【Python探索之旅】列表

目录 特点 入门 访问元素 新增元素 修改元素 插入元素 删除元素 完结撒花 前言 在Python中&#xff0c;列表(List)是最常用的数据结构之一&#xff0c;类似于其他语言&#xff0c;如Java&#xff0c;与其不同啊Python中不需要声明数据类型。它提供了一种灵活且高效的方式…

数学建模入门手册

数模的主要比赛 美赛&#xff08;报名费100美元&#xff0c;每年2月比赛&#xff09;&#xff1b;国赛&#xff08;每年9月&#xff09; 电工杯&#xff08;每年5月&#xff09;&#xff1b;APMCM&#xff08;每年11月&#xff09;&#xff1b;MathorCup&#xff08;每年4月&a…

3.TCP的三次握手和四次挥手

一、前置知识 TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。在传输数据前通信双方必须建立连接&#xff08;所谓连接&#xff0c;是指客户端和服务端各自保存一份关于对方的信息&#xff0c;比如ip地址&#xff0c;端口号等&#xff09;。TCP通过三次握手建立一个…

Spring Boot 整合讯飞星火3.5通过接口Api接口实现聊天功能(首发)复制粘贴即可使用,后续更新WebSocket实现聊天功能

程序员必备网站&#xff1a; 天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 1.pom.xml <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.72</version></dependency><depen…

信噪比(SNR)的基本定义及注意事项

本文介绍信噪比&#xff08;SNR&#xff09;的基本定义及注意事项。 1.基本定义 信噪比&#xff08;SNR&#xff09;&#xff0c;指的是系统中信号与噪声的比&#xff0c;通常用分贝&#xff08;dB&#xff09;表示。 1)用功率来描述 &#xff0c;分别为信号和噪声的功率 2…

Hello,World驱动之旅,用户层简单交互(三)

目录 &#xff08;一&#xff09;上篇回顾&#xff1a;上一篇讲到用户层怎么与驱动模块进行交互。Hello&#xff0c;World驱动之旅&#xff0c;对外接口(二)-CSDN博客 &#xff08;二&#xff09;通过简单程序与驱动交互 读操作&#xff0c;代码如下&#xff1a; 写操作&…

Day28

回溯算法part02 LC组合总和II 终止条件&#xff0c;sumn且mid.size()k加入result。其他终止条件sum>n或者mid.size()>k也结束一样可以引入剪枝操作进行优化&#xff0c;其实sum>n也可以算是剪枝操作的一部分 LC17电话号码的字母组合&#xff08;超时5min&#xff…

复利效应(应用于成长)

应用 每个人在智力、知识、经验上&#xff0c;复利效应都一样&#xff0c;只要能积累的东西&#xff0c;基本上最终都会产生复利效应。 再来看一下复利公式&#xff1a;FP*(1i)^n P本金&#xff1b;i利率&#xff1b;n持有期限。在使用时&#xff0c;一定要注意4个限定条件&a…

网络安全快速入门(十二)(下) 目录结构相关命令补充

12.4 补充命令 我们已经了解了linux的目录结构&#xff0c;接下来我们大概看一下针对目录及文件的一些相关命令&#xff0c; 我们本章只讲三个目录及文件相关的命令&#xff0c;分别是tree&#xff0c;find及校验文件命令&#xff0c;我们一个一个来看这些命令。 12.4.1 tree命…

数学建模——线性回归模型

目录 1.线性回归模型的具体步骤和要点&#xff1a; 1.收集数据&#xff1a; 2.探索性数据分析&#xff1a; 3.选择模型&#xff1a; 4.拟合模型&#xff1a; 5.评估模型&#xff1a; 1.R平方&#xff08;R-squared&#xff09;&#xff1a; 2.调整R平方&#xff08;Ad…

LeetCode 126题:单词接龙 II

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

[初学者来练]用html+css+javascript个人博客作业需求

文章目录 项目概述项目需求页面设计主页文章列表页文章详情页用户交互额外功能&#xff08;可选&#xff09; 技术要求提交要求评分标准文件代码格式提示HTML 页面结构CSS 样式设计JavaScript 交互功能 项目概述 这个项目旨在通过使用HTML、CSS和JavaScript创建一个简单而功能…