数据结构之“队列”(全方位认识)

news2024/10/6 2:53:08

                                    🌹个人主页🌹:喜欢草莓熊的bear

                                    🌹专栏🌹:数据结构


前言

上期博客介绍了”  栈 “这个数据结构,他具有先进后出的特点。本期介绍“ 队列 ”这个数据结构,他具有先进先出的特点。

 

目录

前言

一、队列

1.1队列的概念及结构

1.2队列的实现

1.2.1队列结构体定义

1.2.2初始化和销毁

1.2.3队尾插入和队头删除

1.2.3.1队尾插入

1.2.3.2队头删除

1.2.4获取队头与队尾数据

1.2.5返回队列里面的元素个数

1.2.6队列判空

1.3测试代码

1.4代码展示

头文件

 实现功能文件.c

总结


一、队列

1.1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有 先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为 队尾 出队列:进行删除操作的一端称为 队头。 根据队列的特点我们设计了以下,队尾用来入数据,队头用来出数据。

1.2队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

1.2.1队列结构体定义

根据上面提到的我们基于链表(这里的链表代指单链表)来实现队列。所以我就定义以下结构体

typedef int QDataType;

typedef struct QueueNode//我们基于单链表创建的队列节点
{
	struct QueueNode* next;//下一节点的地址
	QDataType val;
}QNode;

 根据内容发现我们要获取队列的队头数据和队尾数据,所以我们要创建两个这样的结构体。由于节点的地址是通过一级指针来表示的,所以要用二级指针来储存,所以我们传参数时要使用二级指针。 记住只要涉及通过形式参数改变实参的都要传地址。

这时我们很牛的杭哥就给了一个思路,那就是再创建一个结构体里面的元素是上一个结构体。这样既满足了队头和队尾指针,还不需要传二级指针。因为有一个计数的功能所以再定义一个计数的变量。

typedef struct Queue //因为我们要传头指针和尾指针,所以我们要传两个指针
                     //我们又要传二级指针,但是呢很麻烦,所以我们就新创建一个结构体来储存
	                 //上一个结构体的指针。
{
	 QNode* phead;//队头指针
	 QNode* ptail;//队尾指针
	 int size;
}Queue;

1.2.2初始化和销毁

初始化:这一步和上次写的栈的初始化可以说是十分相似,第一步还是判断传来的是否为空队列,把结构体里面的指针置为NULL,再把size赋值位0。就ok了。

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

销毁:因为要申请空间,必定需要free,因为我们是基于链表实现的参考一下单链表的销毁。

 所以我们也需要一个一个节点释放,最后把指针指向空指针,把size赋值为0.

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

	QNode* pur = pq->phead;

	while (pur)
	{
		QNode* cur = pur->next;
		free(pur);
		pur = cur;
	}

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

1.2.3队尾插入和队头删除

1.2.3.1队尾插入

因为我们是基于单链表的实现的,所以只需要创建节点就可以了,通过malloc申请空间。添加一个节点就开辟一块空间。

链表不为空;很简单直接进行尾插操作,不要忘了给size++。

链表为空;那就把头和尾指针同时指向这个新节点。

判别是否为空链表少不了。

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++;//用来计数
}

1.2.3.2队头删除

链表中只有一个节点:当phead == ptail 时就只有一个节点了,所以我们把这个节点释放了以后要把phead 和 ptail 都置为空指针,预防出现野指针。

多个节点:我们创建一个节点用来储存头节点的下一个指针,这样释放了头节点也可以找到剩下的节点。对头删除当然要size--。

老生常谈,判断是否为空链表,还要判断是否还可以进行队头删除操作。也就队列里面还有数据吗

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

	if (pq->phead == pq->ptail)//只有一个节点
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else//多个节点
	{
		QNode* tmp = pq->phead->next;
		free(pq->phead);
		pq->phead = tmp;
	}
	pq->size--;
}

1.2.4获取队头与队尾数据

很简单返回指针里面储存的数据就可以了,除了判断队列是否为空。还要判断这些头尾指针是否为空的。

QDataType QueueFront(Queue* pq) //返回队头数据
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}




QDataType QueueBack(Queue* pq) //返回队尾数据
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}

1.2.5返回队列里面的元素个数

这时就可以体现出我们再第二给结构体中创建size的价值了,我们直接返回size。没有创建size我们就需要遍历队列了。

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

	return pq->size;
}

1.2.6队列判空

前面创建的szie又帮了大忙,还可以用来判断是否为空对列。

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

	return pq->size == 0;
}

1.3测试代码

进行了4个数据的插入,检测了判空、获取队头元素、队头删除。等操作,在这里还是建议大家每完成一个功能就进行测试,不然一起测试出现了很多问题修改起来很麻烦的。作者身有体会,

#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"

int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestory(&q);
	return 0;
}

 成功实现了队列 “ 先进先出 ”的特点。

1.4代码展示

头文件

Queue.h
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>

typedef int QDataType;

typedef struct QueueNode//我们基于单链表创建的队列节点
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue //因为我们要传头指针和尾指针,所以我们要传两个指针
                     //我们又要传二级指针,但是呢很麻烦,所以我们就新创建一个结构体来储存
	                 //上一个结构体的指针。
{
	 QNode* phead;
	 QNode* ptail;
	 int size;
}Queue;

//初始化和销毁
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);

//队尾插入和队头删除
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);

//获取队头、尾数据
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);

//返回个数和判空
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);

 实现功能文件.c

Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

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

	QNode* pur = pq->phead;

	while (pur)
	{
		QNode* cur = pur->next;
		free(pur);
		pur = cur;
	}

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

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++;//用来计数
}

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

	if (pq->phead == pq->ptail)//只有一个节点
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else//多个节点
	{
		QNode* tmp = pq->phead->next;
		free(pq->phead);
		pq->phead = tmp;
	}
	pq->size--;
}

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

	return pq->phead->val;
}

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

	return pq->ptail->val;
}

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

	return pq->size;
}

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

	return pq->size == 0;
}

总结

很感谢大家的喜欢,有问题大家在评论区发来我们一起交流。下期预告通过栈和队列互相实现对方的功能。

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

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

相关文章

ASCII码对照表【2024年汇总】

&#x1f37a;ASCII相关文章汇总如下&#x1f37a;&#xff1a; &#x1f388;ASCII码对照表&#xff08;255个ascii字符汇总&#xff09;&#x1f388;&#x1f388;ASCII码对照表&#xff08;Unicode 字符集列表&#xff09;&#x1f388;&#x1f388;ASCII码对照表&#x…

Linux内核链表使用方法

简介&#xff1a; 链表是linux内核中最简单&#xff0c;同时也是应用最广泛的数据结构。内核中定义的是双向链表。 linux的链表不是将用户数据保存在链表节点中&#xff0c;而是将链表节点保存在用户数据中。linux的链表节点只有2个指针(pre和next)&#xff0c;这样的话&#x…

中国星坤连接器:定制化服务,精准选型!

在当今快速发展的电子行业中&#xff0c;连接器作为电子设备中不可或缺的组成部分&#xff0c;其性能和品质直接影响到整个系统的性能表现。中国星坤连接器以其卓越的产品选型系统和质量保证&#xff0c;为全球客户提供了一站式的解决方案。 精准选型&#xff0c;快速定位 中国…

模板进阶:非类型模板参数,类模板特化,模板的编译分离

1. 非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将该参数当成常…

【python】python母婴数据分析模型预测可视化(数据集+论文+PPT+源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

vue-cli 脚手架详细介绍

4 vue-cli 脚手架 1 脚手架介绍 vue-cli也叫vue脚手架,vue-cli是vue官方提供的一个全局命令工具&#xff0c;这个命令可以帮助我们快速的创建一个vue项目的基础架子。 脚手架&#xff1a;搭建好的一个架子&#xff0c;我们在架子上进行开发 开箱即用零配置基于webpack、webpac…

13 学习总结:指针 · 其一

目录 一、内存和地址 &#xff08;一&#xff09;内存 &#xff08;二&#xff09;内存单元 &#xff08;三&#xff09;地址 &#xff08;四&#xff09;拓展&#xff1a;CPU与内存的联系 二、指针变量和地址 &#xff08;一&#xff09;创建变量的本质 &#xff08;二…

【MySQL】逻辑架构与存储引擎

一、逻辑架构 1、MySQL逻辑架构 我们可以根据上图来对sql的执行过程进行分析 第一步&#xff1a;客户端与服务器建立一个连接&#xff0c;从连接池中分配一个线程处理SQL语句第二步&#xff1a;SQL接口接受SQL指令第三步&#xff1a;如果是5.7版本&#xff0c;就会先去缓存中…

SpringMVC(2)——controller方法参数与html表单对应

controller方法参数与html表单对应 0. User实体类 import org.springframework.format.annotation.DateTimeFormat;import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Map;public class User implements Serializable {private …

期末考试结束,老师该如何私发成绩?

随着期末考试的落幕&#xff0c;校园里又恢复了往日的宁静。然而&#xff0c;对于老师们来说&#xff0c;这并不意味着工作的结束&#xff0c;相反&#xff0c;一系列繁琐的任务才刚刚开始。 成绩单的发放&#xff0c;就是其中一项让人头疼的工作。家长们焦急地等待着孩子的考试…

【全面讲解如何安装Jupyter Notebook!】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

springboot三层架构详细讲解

目录 springBoot三层架构0.简介1.各层架构1.1 Controller层1.2 Service层1.3 ServiceImpl1.4 Mapper1.5 Entity1.6 Mapper.xml 2.各层之间的联系2.1 Controller 与 Service2.2 Service 与 ServiceImpl2.3 Service 与 Mapper2.4 Mapper 与 Mapper.xml2.5 Service 与 Entity2.6 C…

【Spring Boot】关系映射开发(三):多对多映射

关系映射开发&#xff08;三&#xff09;&#xff1a;多对多映射 1.创建实体1.1 创建 Student 实体1.2 创建 Teacher 实体 2.创建测试 在 多对多 关联关系中&#xff0c;只能通过 中间表 的方式进行映射&#xff0c;不能通过增加外键来实现。 注解 ManyToMany 用于关系的发出端…

【React Native优质开源项目】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

Nacos架构设计

Nacos1.X架构设计 Nacos2.X架构修改

Gitlab代码管理工具安装配置

前言&#xff1a; 没有真正的证书与域名建议使用httpip的方式在内网使用&#xff0c;不建议使用假的域名地址 一、安装前配置 #更改主机域名 hostnamectl set-hostname gitlab.dome.com bash #配置hosts 底部添加下面内容 vim /etc/hosts ############################ ip gi…

昇思25天学习打卡营第19天|Diffusion扩散模型

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成…

【嵌入式DIY实例-ESP8266篇】-LCD ST7735显示BMP280传感器数据

LCD ST7735显示BMP280传感器数据 文章目录 LCD ST7735显示BMP280传感器数据1、硬件准备与接线2、代码实现本文介绍如何将 ESP8266 NodeMCU 板 (ESP-12E) 与 Bosch Sensortec 的 BMP280 气压和温度传感器连接。 NodeMCU 微控制器 (ESP8266EX) 从 BMP280 传感器读取温度和压力值,…

人工智能在病理组学虚拟染色中的应用|文献精析·24-07-07

小罗碎碎念 本期文献精析&#xff0c;分享的是一篇关于深度学习在虚拟染色技术中应用于组织学研究的综述。 角色姓名单位&#xff08;中文&#xff09;第一作者Leena Latonen东芬兰大学&#xff08;QS-552&#xff09;生物医学研究所通讯作者Pekka Ruusuvuori图尔库大学&#…

Raw Socket(一)实现TCP三次握手

实验环境&#xff1a; Windows物理机&#xff1a;192.168.1.4 WSL Ubuntu 20.04.6 LTS&#xff1a;172.19.32.196 Windows下的一个http服务器&#xff1a;HFS&#xff0c;大概长这个样子&#xff1a; 客户端就是Ubuntu&#xff0c;服务端就是这个…