数据结构之《队列》

在数据结构之《栈》章节中学习了线性表中除了顺序表和链表外的另一种结构——栈,在本篇中我们将继续学习另一种线性表的结构——队列,在通过本篇的学习后,你将会对栈的结构有充足的了解,在了解完结构后我们还将进行栈的实现。一起加油吧!!!

 


 1.队列的结构与定义

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

入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头

那么在队列的底层结构应该选择的是数组还是链表呢?接下来就来分析看看

在使用数组作为队列的底层结构时,由于队列的性质要求先进先出,所以可以使用size来表示数组内的有效元素个数,这样在入队列时就可以直接在数组的size位置插入数据,但是在出队列时为了将数组内的首个元素也就是数组下标为0的位置移除,这就需要将数组从下标为1的位置开始整体都向前移动一位,所以在出队列的时间复杂度为O(N) 

在使用单链表作为队列的底层结构时,由于队列的性质要求先进先出,所以如果只用一个phead指针指向链表的第一个节点,那么就会使得在入队列时要找到链表的尾节点就需要通过遍历链表才能实现,这就会使得时间复杂度为O(N),所以为了解决以上这种缺陷,就在创建一个指针指向链表的尾节点,在入队列是就可以直接找到尾节点,这样就可以使得无论是入队列还是出队列时间复杂度都为O(1)

通过以上的分析就可以得出在实现队列时底层结构选择单链表是最优解

 

2.队列的实现

在实现队列的代码中,在Queue.h头文件中定义队列的结构以及队列中各个函数的声明,在Queue.c文件内完成各个函数的定义,在test.c文件内对实现的各个函数进行测试

2.1队列结构的定义

在队列结构的定义中,创建一个结构体QueueNode来表示节点,该节点内部有两个成员变量,data来存放节点的数据,next指针来存放下一个节点的地址;之后还需再创建一个结构体Queue来存放指向链表的第一个节点和尾节点的指针,并且在这个结构体内还创建一个成员变量size来表示链表中节点的个数,这样是为了在之后的计算队列有效数据个数时直接将size的值提取出来就可实现了

//队列结构的定义
typedef int QDataType;
typedef struct QueueNode
{QDataType data;//节点内的数据struct QueueNode* next;//指向下一个节点的指针}QueueNode;typedef struct Queue
{struct QueueNode* phead;//指向第一个节点的指针struct QueueNode* ptail;//指向尾节点的指针int size;//节点的个数}Queue;

 

2.2队列的初始化

要完成队列的初始化函数首先要在Queue.h内完成函数的声明

//初始化队列
void QueueInit(Queue* pq);

将该函数命名为QueueInit函数的参数为存放指向头尾节点指针的结构体指针

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言

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

 

2.2判断队列是否为空

要完成队列判空函数首先要在Queue.h内完成函数的声明

bool QueueEmpty(Queue* pq);

将该函数命名为QueueEmpty,函数的为存放指向头尾节点指针的结构体指针,函数的放回类型是布尔类型,当队列为空时放回true,不为空就放回false

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言

在队列为空时就是指向第一个队列的指针phead和指向队列的尾部的指针ptail都为空,所以该函数的返回值就为pq->phead ==NULL && pq->ptail == NULL

//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead ==NULL && pq->ptail == NULL;
}

 

2.3入队列 

要完成入队列函数首先要在Queue.h内完成函数的声明

//入队列
void QueuePush(Queue* pq, QDataType x);

将该函数命名为QueuePush,函数的参数有两个,第一个为存放指向头尾节点指针的结构体指针,第二个为要插入的数据

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言
在实现数据的入队列前要为数据申请新的节点空间,在此使用malloc来实现,申请完之后在将要入队列的数据x存放到新节点newnode中,之后在将新节点插入到链表时要分以下两种情况一种是phead指针指向为空时也就是这时链表内一个节点都没有,这时就要将phead和ptail都newnode;另外一种情况是phead指针指向不为空时,这时就先使得ptail的next指针指向newnode,之后再将ptail指向新节点newnode
最后在完成以上操作后要将有效个数size+1

//入队列
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead== NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;
}

 

2.4出队列 

要完成出队列函数首先要在Queue.h内完成函数的声明

//出队列
void QueuePop(Queue* pq);

将该函数命名为QueuePop函数的参数为存放指向头尾节点指针的结构体指针

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言
同时在出队列时队列内不能一个节点都没有,所以要判断队列内不为空,因此要将!QueueEmpty进行assert断言

之后在出队列也就是删除单链表的第一个节点时分为以下两种情况,第一种是指针phead和指针ptail指向同一个节点也就是链表中只有一个节点,这时就直接释放该节点,之后再将phead和ptail置为空;另外一种情况是指针phead和指针ptail不相同,这时就先创建一个新的指针变量Next指向原来的phead的next指针指向的节点,之后将phead指向的节点释放后,在将phead指针置为Next指向的节点

最后在完成以上操作后要将有效个数size-1

//出队列
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* Next = pq->phead->next;free(pq->phead);pq->phead = Next;}pq->size--;}

 

2.5取队列头数据 

要完成取队列头数函数首先要在Queue.h内完成函数的声明

//取队列头数据
QDataType QueueFront(Queue* pq);

将该函数命名为QueueFront函数的参数存放指向头尾节点指针的结构体指针,函数的放回值就为队列的头数据也就是单链表的第一个节点存放的数据

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言
同时在出队列时队列内不能一个节点都没有,所以要判断队列内不为空,因此要将!QueueEmpty进行assert断言
在队列中的头数据就为phead指针指向的节点内存放的数据
因此该函数直接放回pq->phead->data即可

//取队列头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}

 

2.6取队尾数据

要完成取队列尾数据函数首先要在Queue.h内完成函数的声明

//取队列尾数据
QDataType QueueBack(Queue* pq);

将该函数命名为QueueBack函数的参数为存放指向头尾节点指针的结构体指针,函数的放回值就为队列的尾数据也就是单链表的最后一个节点存放的数据

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言
同时在出队列时队列内不能一个节点都没有,所以要判断队列内不为空,因此要将!QueueEmpty进行assert断言
在队列中的尾数据就为ptail指针指向的节点内存放的数据
因此该函数直接放回pq->ptail->data即可

//取队列尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}

 

2.7队列有效数据个数 

要完成队列有效数据个数函数首先要在Queue.h内完成函数的声明

//队列有效数据个数
int QueueSize(Queue* pq);

将该函数命名为QueueSize函数的参数为存放指向头尾节点指针的结构体指针,函数的返回值就为队列内有效的数据个数

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言
因为队列内的有效数据个数就为结构体Queue内的成员变量size的值
所以该函数直接放回pq->size即可

//队列有效数据个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

 

2.8队列的销毁 

要完成队列的销毁函数首先要在Queue.h内完成函数的声明

//销毁队列
void QueueDestory(Queue* pq);

将该函数命名为QueueDestory, 函数的参数为存放指向头尾节点指针的结构体指针

完成了函数的声明接下来就是在Queue.c函数内完成函数的定义

因为以下函数中的pq指针是存放指向头尾节点指针的结构体指针,该指针不能为空,因此要将pq进行assert断言
同时在出队列时队列内不能一个节点都没有,所以要判断队列内不为空,因此要将!QueueEmpty进行assert断言

在销毁过程中通过遍历的方法来用free释放链表的所有节点,最后再将指针phead和指针ptail置为空,将有效数据个数赋值为0

//销毁队列
void QueueDestory(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QueueNode* pcur = pq->phead;while (pcur){QueueNode* Next = pcur->next;free(pcur);pcur = Next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}

 

3.队列实现完整代码 

Queue.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>//队列结构的定义
typedef int QDataType;
typedef struct QueueNode
{QDataType data;//节点内的数据struct QueueNode* next;//指向下一个节点的指针}QueueNode;typedef struct Queue
{struct QueueNode* phead;//指向第一个节点的指针struct QueueNode* ptail;//指向尾节点的指针int size;//节点的个数}Queue;
//初始化队列
void QueueInit(Queue* pq);
//销毁队列
void QueueDestory(Queue* pq);
//入队列
void QueuePush(Queue* pq, QDataType x);
//出队列
void QueuePop(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//取队列头数据
QDataType QueueFront(Queue* pq);
//取队列尾数据
QDataType QueueBack(Queue* pq);
//队列有效数据个数
int QueueSize(Queue* pq);

 

Queue.c

#include"Queue.h"//初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}//销毁队列
void QueueDestory(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QueueNode* pcur = pq->phead;while (pcur){QueueNode* Next = pcur->next;free(pcur);pcur = Next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead ==NULL && pq->ptail == NULL;
}//入队列
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;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(!QueueEmpty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{QueueNode* Next = pq->phead->next;free(pq->phead);pq->phead = Next;}pq->size--;}//取队列头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}//取队列尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;}//队列有效数据个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

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

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

相关文章

【LLM】-08-搭建问答系统-语言模型,提问范式与 Token

目录 1、语言模型 1.1、训练过程&#xff1a; 1..2、大型语言模型分类&#xff1a; 1.3、指令微调模型训练过程&#xff1a; 2、Tokens 3、Helper function辅助函数 (提问范式) 4、计算token数量 1、语言模型 大语言模型&#xff08;LLM&#xff09;是通过预测下一个词…

一款允许使用Docker部署本地托管的、基于 Web 的 PDF 操作工具

大家好&#xff0c;今天给大家分享的是一个基于Spring Boot开发的开源项目&#xff0c;旨在提供一个功能强大的基于Docker的本地托管PDF操作工具Stirling PDF。 项目介绍 Stirling-PDF是一个全面的PDF工具箱&#xff0c;适用于个人和企业用户&#xff0c;尤其对于那些重视数据…

CasaOS设备使用Docker安装SyncThing文件同步神器并实现远程管理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

C++树形结构(1 基础)

目录 一.基础&#xff1a; 1.概念&#xff1a; 2.定义&#xff1a; Ⅰ.树的相关基础术语&#xff1a; Ⅱ.树的层次&#xff1a; 3.树的性质&#xff1a; 二.存储思路&#xff1a; 1.结构体存储&#xff1a; 2.数组存储&#xff1a; 三.树的遍历模板&#xff1a; 四.信…

用 python scipy 库模拟拥塞控制模型

接着昨天的继续说&#xff0c;参见 inflight 守恒建模。 欧拉数值解看起来不够优雅&#xff0c;所以我打算找个别的方式试一下&#xff0c;顺便学一下 python&#xff0c;我不会编程&#xff0c;但也不是一点也不会&#xff0c;我稍微会一点&#xff0c;所以想进一步学习一点。…

记录unraid docker更新的域名

背景&#xff1a;级联 一、安装内容 unraid更新docker&#xff0c;之前一直失败&#xff0c;修改网络后可以进行安装。 二、查看域名 查看域名&#xff0c;发现是走github的&#xff0c;怪不得有一些docker无法正常更新 三、解决方法 更改代理&#xff0c;这里为unraid的…

STM32智能城市交通管理系统教程

目录 引言环境准备智能城市交通管理系统基础代码实现&#xff1a;实现智能城市交通管理系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;城市交通管理与优化问题解决方案与优化收尾与总结 1. 引言 智能城…

Vue2高级用法

Vue2高级用法 1、mixin复用【vue不会用了&#xff0c;了解一下】1.1 基础使用1.2 选项合并1.3 全局混入1.4 细数 mixin 存在的问题 2、vue.js 动画特效&#xff06; 常见组件库介绍2.1 进入/离开基础使用示例2.2 进入/离开自定义过度类名2.3 进入/离开动画钩子2.4 多组件过渡与…

c++树(一)定义,遍历

目录 树的定义 树的基本术语 树的初始起点&#xff1a;我们定义为根 树的层次&#xff1a; 树的定义&#xff1a; 树的性质 性质1&#xff1a; 性质2&#xff1a; 树形结构存储的两种思路 树的遍历模板 树上信息统计方式1-自顶向下统计 树上信息统计方式2-自底向上统…

【UbuntuDebian安装Nginx】在线安装Nginx

云计算&#xff1a;腾讯云轻量服务器 操作系统&#xff1a;Ubuntu-v22 1.更新系统软件包列表 打开终端并运行以下命令来确保你的系统软件包列表是最新的&#xff1a; sudo apt update2.安装 Nginx 使用以下命令安装 Nginx&#xff1a; sudo apt install nginx3.启动 Nginx…

Docker-Compose配置zookeeper+KaFka+CMAK简单集群

1. 本地DNS解析管理 # 编辑hosts文件 sudo nano /etc/hosts # 添加以下三个主机IP 192.168.186.77 zoo1 k1 192.168.186.18 zoo2 k2 192.168.186.216 zoo3 k3注&#xff1a;zoo1是192.168.186.77的别名&#xff0c;zoo2是192.168.186.18的别名&#xff0c;zoo3是192.168.186.1…

企元数智引领新零售合规分销系统免费送

企元数智近日宣布推出全新的新零售合规分销系统&#xff0c;并免费向企业提供这一创新解决方案。这一举措旨在帮助更多企业实现数字化转型&#xff0c;提高管理效率&#xff0c;促进业务增长。 新零售合规分销系统是企元数智引领的一项全新数字解决方案&#xff0c;涵盖了销售数…

Linux第四节课(指令与权限)

1、date指令(时间) 程序运行到自己的每一个关键时刻&#xff0c;都要自己打日志&#xff01; 日志包括时间、日志等级、日志具体信息、其他信息等&#xff0c;然后按照行为单位写入文件中&#xff0c;这个文件被称为日志文件&#xff01; 在日志文件中筛选信息时&#xff0c…

【Unity国产化信创平台】虚拟机VMware Workstation Pro虚拟机下载安装

目录 一、虚拟机软件VMware Workstation Pro下载 二、虚拟机安装流程 1.傻瓜式安装 2.是否自动安装WHP 一、虚拟机软件VMware Workstation Pro下载 https://www.vmware.com/products/desktop-hypervisor/workstation-and-fusion 官网各种访问出错&#xff0c;下载界面总是…

H3CNE(vlan与子接口技术)

目录 10.1 vlan间通信技术 示例一&#xff08;多臂路由&#xff09;&#xff1a; 10.2 子接口技术 示例二&#xff08;子接口技术&#xff09;&#xff1a; 10.3 vlannif接口技术 10.3.1 三层交换机与VLANNIF技术 示例三VLANNIF配置&#xff08;将交换机当成路由器使用&…

DLMS/COSEM中公开密钥算法的使用_椭圆曲线加密法

1.概述 椭圆曲线密码涉及有限域上的椭圆曲线上的算术运算。椭圆曲线可以定义在任何数字域上(实数、整数、复数)&#xff0c;但在密码学中&#xff0c;椭圆曲线最常用于有限素数域。 素数域上的椭圆曲线由一组实数(x, y)组成&#xff0c;满足以下等式: 方程的所有解的集合构成…

Go语言---list的声明、常用方法以及遍历

List所在的库 Go语言的链表实现在其标准库的container/list代码包中。 import "container/list"这个包含了两个公开的程序实体&#xff1a;List和Element。 List代表一个双向链表&#xff0c;其零值为一个空的、可用的链表&#xff1b;Element代表双向链表中的一个…

Python | Leetcode Python题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; class Solution { public:// 判断是否为完全平方数bool isPerfectSquare(int x) {int y sqrt(x);return y * y x;}// 判断是否能表示为 4^k*(8m7)bool checkAnswer4(int x) {while (x % 4 0) {x / 4;}return x % 8 7;}int numSquares(i…

注册邮箱需要实名认证吗

注册邮箱需要进行实名认证吗&#xff1f;都需要认证哪些信息呢&#xff1f;邮箱实名认证提高安全性和责任感&#xff0c;但可能涉及隐私顾虑。本文将详细介绍邮箱实名认证的相关内容。 一、邮箱实名认证的定义和目的 1、定义 电子邮件实名验证是指客户在注册电子邮件服务时&…

初阶数据结构之栈和队列

栈和队列是两种特殊的线性表&#xff0c;都可以用数组或者链表来实现&#xff0c;接下来就让我们看看栈和队列会有什么奥秘吧~ 目录 1.栈 1.1栈的概念 1.2栈的实现 1.2.1 Stack.h 1.2.2 Stack.c 1.2.3 test.c 2.队列 2.1队列的概念 2.2队列的实现 2.2.1 Queue.h 2…