c语言实现队列

文章目录

  • 前言
  • 一、队列的特征
  • 二、队列的实现
    • 1、队列的设计
    • 2、队列的初始化
    • 3、元素的入队和出队
    • 4、返回队头的数据和队尾的数据
    • 5、返回队列的长度
    • 6、队列的销毁
  • 三、循环队列
  • 四、队列和栈综合练习


前言

栈的特点是元素后进先出(Last In First Out),而对应的还有一种数据结构,该结构的特点是先进先出(First In First Out),即为队列。

一、队列的特征

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

二、队列的实现

1、队列的设计

队列可以使用数组或链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。因为队列需要尾插头删,当数组头删时,需要全部元素向前移动一位,时间复杂度为O(N),而使用链表来实现队列的话,头删和尾插元素的时间复杂度都是O(1)。

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

typedef int QDataType;
//队列的结点设计
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

//创建一个Queue结构体变量,就相当于创建了一个队列,该结构体变量中存的有该队列的头结点地址,尾结点地址
//所以当有该结构体变量的地址时,可以通过该地址改变队列的头结点地址和尾结点地址,即改变head指针和tail指针。
typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

2、队列的初始化

因为队列中需要有一个头指针用来记录队头结点的地址,一个尾指针用来记录队尾结点的地址。所以又创建了一个Queue结构体,该结构体中有两个QNode类型的成员变量,即Queue结构体中定义了两个结构体指针变量,一个用来存储队列的队头结点的地址,一个用来存储队尾结点的地址。
结构体Queue里面就只有两个QNode
类型的结构体指针,所以当想创建一个队列时,创建一个Queue类型的结构体变量s就好了,此时Queue类型的结构体变量s的成员head和tail里面存的都是随机的地址,所以需要初始化,则需要调用QueueInit()函数,并且将结构体变量s的地址传进去,因为只有传s的地址进去,函数里面才可以根据s的地址找到s的成员head和tail,然后才能改变head和tail里面存储的随机地址的值。
在这里插入图片描述

如果传的是s的话,那么函数的形参就设置为Queue pq,那么在函数中会临时创建一个Queue类型的结构体变量pq,然后将s的值拷贝到pq中,此时在函数中修改pq.head和pq.tail时,外面s的head和tail并不会改变,所以要传s的地址。
队列初始化就是通过s的地址 0x 1234找到s,然后改变s的成员变量head和tail的值为0x 0000。

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

在这里插入图片描述
在这里我们可以再看一下单链表的设计。
单链表中结构体的定义为

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

然后单链表在使用时,需要先创建一个SLTNode类型的结构体指针,然后再将该结构体指针的地址传入函数中。而函数中的形参都设计了二级指针来接收这个SLTNode类型的结构体指针的地址。这样设计是因为当单链表为空时,此时单链表的头结点指针ps就指向NULL,而当要给单链表插入元素时,如果只将ps传进去,那么在函数中形参只需设计为一级指针SLTNode*即可,但是这样设计的话,并不会改变ps的值,改变的只是函数中临时创建的和ps类型一致的一个指针变量的值。此时主函数的ps的值依旧为NULL,所以要向改变ps的值,只能将ps的地址传进去,然后函数中形参设计为二级指针,在函数中可以通过解引用ps的地址来找到ps,然后将ps的值设置为单链表结点的地址。
在这里插入图片描述
可以看到在前面的单链表中,我们用到了二级指针,但是为什么队列没有用二级指针呢?因为队列不止要有一个指针变量存储队头结点的地址,还需要一个指针变量来存储队尾结点的地址,而改变时需要改变存储队头结点的地址的指针head的值,也需要改变存储队尾结点的地址的指针tail的值。而改变这两个指针的值,就需要函数设计两个二级指针的形参,所以我们为了方便直接设计了一个结构体Queue,结构体的成员包含两个指针变量。
单链表的话就只需改变一个指向单链表头结点的指针的值,所以可以直接使用二级指针来实现。

3、元素的入队和出队

队列中元素在入队时我们要判断此时队列是否为空,如果队列为空的话,我们就需要将队头指针和队尾指针都指向入队的这个元素。如果队列不为空的话,此时就在队尾指针指向的队尾结点后插入这个新元素即可。然后将队尾指针指向这个新插入的结点,以保证队尾指针指向的总是队列的队尾结点。

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = newNode;
		pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

队列中元素在出队时需要先判断队列是否为空,如果队列为空,则出队失败。判断队列是否为空,就是判断队列的队头指针或队尾指针的值是否为NULL。

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	/*if (pq->head == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}*/

	return pq->head == NULL;
}

然后在队列中有元素要出队时,先判断队列是否为空,不为空才可以让队列中队头指针指向的结点出队,并且当队列中只有一个结点时,要进行出队的话,这一个结点出队之后,虽然head指针指向了NULL,但是tail指针还指向了这个被释放空间的原来的队尾结点,所以此时tail为野指针,这就需要我们再判断当队列中只有一个结点要出队时,该结点完成出队后,需要将head指针和tail指针都置为NULL,以恢复初始时队列为空的状态。



void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//当队列中只有一个结点时,该结点出队后队列就为空了
	//所以需要将队列的头指针和尾指针都置为NULL
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = NULL;
		//虽然按照下面的处理pq->head也会为NULL,
		//但tail指针还指向已经释放空间的最后一个结点的地址,所以此时tail为野指针,所以需要特别处理,将tail置为NULL
		pq->tail = NULL;
	}
	else
	{
		QNode* del = pq->head;
		pq->head = pq->head->next;
		free(del);
	}
	
}

4、返回队头的数据和队尾的数据

队列还需要提供返回队头结点数据和队尾结点数据的函数,返回队头结点数据就是先判断队列是否为空,不为空的话将队头指针所指向结点的数据返回即可。

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

返回队尾结点数据就是将队尾指针所指向结点的数据返回。

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

5、返回队列的长度

返回队列的长度可以通过遍历队列,每遍历一个结点,就将长度加1,最后将队列长度返回即可。不过这样求队列长度的时间复杂度为O(N)。

int QueueSize(Queue* pq)
{
	assert(pq);
	int size = 0;
	QNode* curr = pq->head;
	while (curr != NULL)
	{
		size++;
		curr = curr->next;
	}

	return size;
}

还可以在设计队列时,将Queue结构体中再加一个成员size,用来记录队列的长度,当有元素入队时pq->size++,当有元素出队时pq->size–,这样队列长度直接通过pq->size就可以得到。

typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;  //用来记录队列长度
}Queue;

6、队列的销毁

队列的销毁就是先将队列的结点都一一销毁,然后将pq->head和pq->tail都指向NULL,此时队列中申请的结点占用的空间都被释放,而且队列回到了最初的状态。

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* curr = pq->head;
	while (curr)
	{
		QNode* next = curr->next;
		free(curr);
		curr = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
	//在这里面将pq置为空没用,因为pq只是临时创建的一个Queue*类型的结构体指针
	//pq里面存的是Queue结构体变量的地址,在函数里面将pq置为NULL对外面没有影响。
	//只是让pq指向不了这个结构体变量了,但是这个结构体变量还存在,
}

三、循环队列

环形队列我们也可以通过一个例题来体会。
循环链表

四、队列和栈综合练习

在学习了队列和栈之后,我们可以用队列来实现栈,或用栈来实现队列,下面的链接为这两个问题的具体实现,可以点击链接进行学习。
用栈实现队列
用队列实现栈

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android GreenDao数据库升级(附Demo)

前言 大家好久不见&#xff0c;一转眼马上八月份下旬了&#xff0c;最近由于工作比较忙&#xff0c;没时间给大家更新博文。百忙之中抽出时间&#xff0c;给大家来更新一篇关于GreenDao3数据库的升级。 关于GreenDao的详细介绍以及一些逻辑性的增、删、改、查等&#xff0c;可以…

3:Ubuntu上配置QT交叉编译环境并编译QT程序到Jetson Orin Nano(ARM)

1.Ubuntu Qt 配置交叉编译环境 1.1 ubuntu 20.04安装Qt sudo apt-get install qtcreator 1.2 配置QT GCC配置同上 最后配置Kits 上面设置完成之后 &#xff0c;设置Kits 中的Device(这是为了能够直接把项目部署到arm设备上) 点击NEXT之后会出现连接被拒绝&#xff0c;不用担…

ARM-汇编指令

一&#xff0c;map.lds文件 链接脚本文件 作用&#xff1a;给编译器进行使用&#xff0c;告诉编译器各个段&#xff0c;如何进行分布 /*输出格式&#xff1a;32位可执行程序&#xff0c;小端对齐*/ OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",…

React原理 - React Virtual DOM 原理

目录 扩展学习资料 Virtual DOM 是什么【虚拟dom】 React渲染 Virtual DOM VS 原生DOM【vDom是否比原生Dom更高效】 Virtual DOM数据结构 Virtaual DOM Diff【虚拟dom前后比对&#xff0c;更新不同dom的算法】 源码解读 react源码组织方式&#xff1a; React Stack Rec…

YOLOv5基础知识入门(7)— NMS(非极大值抑制)原理解析

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。NMS是指非极大值抑制&#xff08;non maximum suppression&#xff09;&#xff0c;它是一种常用于物体检测任务的算法。在物体检测中&#xff0c;通常会有多个预测框&#xff08;bounding box&#xff09;被提议出来&…

开始MySQL探索——数据库概述

计算机语言 计算机语言概述 计算机语言&#xff08;Computer Language&#xff09;可以简单的理解为一种计算机和人都能识别的语言。 机器语言 汇编语言 高级语言 机器语言 汇编语言 高级语言 SQL语言基础 SQL的概述 SQL全称&#xff1a;Structured Query Language&…

Aspose.Tasks for .NET V23Crack

Aspose.Tasks for .NET V23Crack 改进了大型项目的内存占用。 添加了API&#xff0c;允许您在应用程序无法访问系统字体文件夹时指定用户的字体文件夹。 Aspose.Tasksfor.NET是处理MicrosoftProject文件的可靠的项目管理API。API支持在不依赖Microsoft Project的情况下读取、写…

平安私人银行慈善沙龙广州站:善财传承公益有道,广州分行聚爱同行

近年来&#xff0c;平安私人银行将慈善作为客户服务的王牌权益之一&#xff0c;激发和满足客户公益慈善心愿&#xff0c;打造财富人群和困境人群的桥梁&#xff0c;并链接公益机构等专业组织&#xff0c;深度挖掘金融赋能慈善的多种可能性&#xff0c;让财富通过慈善事业释放出…

在router中使用pinia、在组件外使用pinia时 报错没有激活pinia

getActivePinia was called with no active Pinia. Did you forget to install pinia? 我想在路由守卫中使用store中部的数据&#xff0c;但是拿不到仓库&#xff0c;提示pinia没激活 解决方案&#xff1a;借鉴vben-admin 在每个模块中都把pinia和当前的仓库绑定一份暴漏出去…

ARM--day5(C语言点灯实验、总线、串口通信信息、串口通讯协议)

函数分装实现点灯 gpio.c: #include "gpio.h" //函数功能&#xff1a;GPIO引脚初始化操作 //参数1&#xff1a;GPIO组号 //参数2&#xff1a;引脚编号 //参数3&#xff1a;初始化内容 void hal_gpio_init(volatile gpio_t*gpiox,unsigned int pin,gpio_init_t* ini…

net start Mysql 启动服务时 ,显示“Mysql服务正在启动 Mysql服务无法启动 服务没有报告任何错误

一、问题 有时候&#xff0c;输入net start Mysql 启动服务时 mysql>net start Mysql 显示 Mysql服务正在启动 Mysql服务无法启动 服务没有报告任何错误 二、原因 由于mysql的默认端口是3306&#xff0c;因此在启动服务的时候&#xff0c;如果此端口被占用&#xff0c;就会出…

VB.NET通过VB6 ActiveX DLL调用PowerBasic及FreeBasic动态库

前面说的Delphi通过Activex DLL同时调用PowerBasic和FreeBasic写的DLL&#xff0c;是在WINDOWS基础平台上完成的。 而 .NET平台是架在WINDOWS基础平台之上的&#xff0c;它的上面VB.NET或C#等开发的APP程序&#xff0c;下面写一下用VB.NET&#xff0c;通过VB6注册的Activex DLL…

Ubuntu20.04安装软件报错:The following packages have unmet dependencies

Ubuntu20.04更换阿里云源后安装软件都会报错&#xff1a;The following packages have unmet dependencies 查看资料&#xff0c;大概是ubuntu本身的源比较版本较老&#xff0c;而阿里云的源比较新&#xff0c;因此版本不匹配造成依赖的库不匹配&#xff0c;所以只要将阿里云的…

vue 简单实验 数据更新

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"counter">Counter: {{ counter }} </div> <script> const Counter {data() {return {counter: 5}},mounted() {set…

基于Jenkins CICD的代码发布与回滚-------从小白到大神之路之学习运维第87天

第四阶段提升 时 间&#xff1a;2023年8月24日 地 点&#xff1a;2304教室 授课人&#xff1a;李凤海 参加人&#xff1a;全班人员 内 容&#xff1a; 基于Jenkins CICD的代码发布与回滚 目录 一、案例概述 二、案例知识点 三、案例环境 &#xff08;一&#xff0…

PHP“牵手”拼多多商品详情数据获取方法,拼多多API接口批量获取拼多多商品详情数据说明

拼多多商品详情接口 API 是开放平台提供的一种 API 接口&#xff0c;它可以帮助开发者获取拼多多商品的详细信息&#xff0c;包括商品的标题、描述、图片等信息。在拼多多电商平台的开发中&#xff0c;拼多多详情接口 API 是非常常用的 API&#xff0c;因此本文将详细介绍拼多多…

领域建模之数据模型设计方法论

本文通过实际业务需求场景建模案例&#xff0c;为读者提供一种业务模型向数据模型设计的方法论&#xff0c;用于指导实际开发中如何进行业务模型向数据模型转化抽象&#xff0c;并对设计的数据模型可用性、扩展性提供了建议性思考。通过文章&#xff0c;读者可以收获到业务模型…

POI groupRow 折叠分组,折叠部分不显示问题

折叠组是什么&#xff1f;如图就是用POI 实现的&#xff0c;代码很简单&#xff1a;sheet.groupRow(开始行&#xff0c;结束行)即可 但是万万没想到&#xff0c;最终实现出的结果&#xff0c;合并的组&#xff0c;有一部分并没有渲染出来&#xff0c;如下图&#xff1a; 因为我…

线性代数的学习和整理9(草稿-----未完成)

3.3 特征值和特征向量是什么&#xff1f; 直接说现在&#xff1a;特征向量这个块往哪个方向进行了拉伸&#xff0c;各个方向拉伸了几倍。这也让人很容易理解为什么&#xff0c;行列式的值就是特征值的乘积。 特征向量也代表了一些良好的性质&#xff0c;即这些线在线性变换后…

基于GPT-4和LangChain构建云端定制化PDF知识库AI聊天机器人

参考&#xff1a; GitHub - mayooear/gpt4-pdf-chatbot-langchain: GPT4 & LangChain Chatbot for large PDF docs 1.摘要&#xff1a; 使用新的GPT-4 api为多个大型PDF文件构建chatGPT聊天机器人。 使用的技术栈包括LangChain, Pinecone, Typescript, Openai和Next.js…