【数据结构】队列的实现

0. 前言

上期博客给大家讲解了 栈 以及 栈的实现,今天再给大家讲一个特殊的顺序表结构,那就是队列

下面就进入正题!一起学习一下吧!

1. 队列

1.1 队列的概念

队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,

队列具有先进先出FIFO (First In First Out)

入队列:进行插入操作的一端称为队尾

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

其实队列,换一种说法就相当于我们生活中的 排队问题,不管干什么一般总是遵守先来后到的,
就是先来的(对头)先获取到资源,后来的不准插队,只能在最后面(队尾)排队等待。

1.2 队列的结构

它的结构如图所示:
 

2. 队列的实现 

和栈的实现类似的想法,我们实现队列,既可以用数组实现,也可以用链表的结构实现。

如下图所示:

但是我们需要考虑的是,之前学习的哪一种结构可以更高效的实现我们的队列呢?

其实我们对比顺序表和链表的优缺点,

链表:空间的利用率高,不用扩容,头插头删高效

顺序表:空间利用率低,需要扩容,但是尾插尾删效率高

针对队列的结构,我们发现

因为如果使用数组的结构,出队列在数组头上出数据,也就是对顺序表进行删除的操作时,

需要挪动数据。效率是比较低的。 

所以我们会选择使用链表来进行 队列的实现。

2.1 准备工作

还是像往常一样,我们将队列其拆分为不同的文件进行设计

1️⃣:Queue.h 文件,用于函数声明
2️⃣:Queue.c 文件,用于函数的定义

3️⃣:Test.c   文件,用于测试函数

2.2  队列 结构体的定义

队列的结构体定义跟链表结构体定义类似:

//队列节点 
typedef struct QueueNode
{struct QueueNode* next;//下一个节点的地址QDataType val;//节点存的数据
}QNode;

除了节点定义之外,我们可以再定义一个队列的结构体类型,

队列的结构体由三个部分组成:

  • phead是一个指向QNode结构体的指针,用于指向队列的队头元素。
  • ptail也是一个指向QNode结构体的指针,用于指向队列的队尾元素。
  • size表示队列中当前元素的个数。
typedef struct Queue
{QNode* phead;//队头指针QNode* ptail;//队尾指针int size;//队列中的元素个数
}Queue;

有细心的同学会很好奇,

为什么我们还需要定义一个Queue这样的结构体类型呢?

队头和队尾定义在刚才定义在QueueNode这个队列节点结构体类型不好吗?

也许是这样的结构体设计

//队列节点 
typedef struct QueueNode
{struct QueueNode* next;//下一个节点的地址QDataType val;//节点存的数据
}QNode, *phead, *ptail;
//对struct QueueNode 重命名为QNode,//根据队列节点类型,定义的队头指针phead和队尾指针ptail

能想到这样问题的同学,很优秀!👍👍👍

其实我们在定义链式队列结构体,也不是非得必须有如上这两个结构体的,

只不过有上面这两个结构体只是在调用函数时方便参数传递

我们在实现链表中也遇到过,对于链表的操作,插入删除操作时,指向链表的头指针可能会发生改变,我们在函数调用时,参数需要用二级指针接收。

这里也是同样的,在队列中插入元素,或者删除时,我们的队头指针phead和队尾指针rear会发生改变。我们在函数调用时,参数同样需要用二级指针接收。同学们会很容易犯同样的错误~

但如果再构造一个队列结构体Queue 我们的队头指针和队尾指针就是我们的结构体成员,

无论Queue这个类型有多少指向结点的指针,直接传递一个指向链队结点的类型的指针就可以了,

这样Queue这个类型这些指向结点的指针一并就传入了,如果想使用,我们可以通过结构体指针访问操作符,也就是这个箭头“->”访问,你看这样是不是就很方便了?

typedef int QDataType;typedef struct QueueNode
{struct QueueNode* next;QDataType val;
}QNode;typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;

这是我们在实现链式队列的一个小优化方式,同学们是不是get到了?是不是很妙呀?

2.3 队列初始化

初始化就是将记录的头指针和尾指针置空,元素个数置暂时赋为0

代码如下:

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

2.4 队尾插入

进行入队操作的时候,我们首先需要判断一下队列是否为空,

如果队列为空的话,需要将头指针和尾指针一同指向第一个结点,

当如果队列不为空的时候,入队列时,我们可以创建一个新节点,并放在记录好的尾节点的位置之后,成为新的尾节点,最后元素个数增加。

代码如下:

// 队尾插入
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->ptail == NULL){pq->phead = pq->ptail = newnode;}//队列不为空else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;//插入数据之后不要忘了把元素个数+1
}

2.5 队头删除

我们分以下情况讨论,

在队列不为空的情况下,进行一个判断,如图所示,如果队列只有一个元素了(即头尾指针均指向了同一个结点),直接将头尾两指针制空(NULL)并释放这一个结点即可。  

当队列含有2个以上元素时,我们需要将队列的头指针指向头指针当前指向的下一个元素并释放掉当前元素即可。最后元素个数减少。如图所示

代码如下:

void QueuePop(Queue* pq)
{assert(pq);assert(pq->size != 0);// 一个节点if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else // 至少2个节点{QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;
}

 2.6 返回队头元素

返回我们记录的头节点的值即可。

代码如下:

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

2.7  返回队尾元素

返回我们记录的尾节点的值即可。

代码如下:

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

2.8 队列元素个数

返回我们记录的元素个数即可。

代码如下:

int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

2.9 队列判空

返回我们记录的元素个数是否为零。

代码如下:

bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}

2.10 队列销毁

类似于单链表的销毁,依次销毁每一个节点后将头尾置空,将元素个数置空。

代码如下:

void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}

3. 完整代码:

Queue.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.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 QueueDestroy(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);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1#include"Queue.h"void QueueInit(Queue* pq)
{assert(pq);pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->phead;while (cur){QNode* next = cur->next;free(cur);cur = next;}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->ptail == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = newnode;}pq->size++;
}// 队头删除
void QueuePop(Queue* pq)
{assert(pq);assert(pq->size != 0);/*QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;if (pq->phead == NULL)pq->ptail = NULL;*/// 一个节点if (pq->phead->next == NULL){free(pq->phead);pq->phead = pq->ptail = NULL;}else // 多个节点{QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}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;
}

test.c

#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);}printf("\n");return 0;
}
代码运行界面

 以上就是对于数据结构队列的基本实现,

4. 总结

这期我们学习的队列和上期博客学习的栈,其实有很多相似之处,尽管栈是队头进入删除数据(后进先出),队列是队尾入数据,队头删数据(先进先出),但其本质是一样的。我们用顺序表实现栈,用链表实现的队列,希望大家对于前面的顺序表和链表的理解更加深刻哦~

 以上就是本期博客的全部内容,如果有疑惑,有问题的小伙伴,欢迎评论区和我探讨哦!

同时我还会继续更新数据结构更多的知识,分享给更多小伙伴,请继续关注我哦!😄😄

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

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

相关文章

多媒体技术及应用课程思政网站

摘 要 在Internet高速发展的今天&#xff0c;我们生活的各个领域都涉及到计算机的应用&#xff0c;其中包括多媒体技术及应用课程思政网站的网络应用&#xff0c;在外国多媒体技术及应用课程思政已经是很普遍的方式&#xff0c;不过国内的多媒体技术及应用课程思政可能还处于起…

WPF Mvvm

了解MVVM 什么是MVVM&#xff1a;一种设计模式 设计模式&#xff08;Design pattern&#xff09;代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人…

网络招商是什么意思,主要干什么工作?

互联网时代&#xff0c;网络招商&#xff0c;已经是企业不可缺少的一部分&#xff0c;这篇文章&#xff0c;详细为大家分享&#xff0c;网络招商的意思&#xff0c;以及主要工作内容&#xff01; 一、网络招商意思 就是利用互联网进行招商的一种方式&#xff0c;借助互联网的…

【深度学习】直观理解AUROC

文章目录 前言如何计算直观解释常用计算方式 前言 AUROC常用于衡量二分类分类器的性能&#xff0c;本文旨在详解该指标计算过程 如何计算 设想我们有一个分类器&#xff0c;对数据做二分类。我们设输入数据为 x x x, 预测标签为 y y y, ground-truth标签为 y ^ \hat{y} y…

docker 拉取镜像 error pulling image configuration: download failed ****

问题&#xff1a; 在安装docker后拉取镜像的时候 出现error pulling image configuration: download failed ****异常 原因&#xff1a; 由于镜像源配置的问题引起的镜像拉取异常 解决 由于网络或者其他原因导致拉取镜像请求失败&#xff0c;报错&#xff1a; error pulli…

Oracle 用户-表空间-表之间关系常用SQL

问题&#xff1a; 当某一个表数据量特别大&#xff0c;突然插入数据一直失败&#xff0c;可能是表空间不足&#xff0c;需要查看表的使用率 用户-表空间-表之间关系&#xff1a;用户可以有多个表空间&#xff0c;表空间可以有多个表&#xff0c;表只能拥有一个表空间和用户 1.…

爱心动画代码HTML5

这段代码是一个HTML5 Canvas动画&#xff0c;它通过JavaScript创建了一个动态的爱心效果。页面初始化时&#xff0c;首先定义了一些基本设置&#xff0c;如粒子数量、持续时间、速度等。然后&#xff0c;定义了Point和Particle类&#xff0c;用于处理粒子的位置和运动。接着&am…

C语言数据类型和变量

数据类型介绍 数据类型介绍 C语言提供了丰富的数据类型来描述生活中的各种数据。 使用整型类型来描述整数&#xff0c;使用字符类型来描述字符&#xff0c;使用浮点型类型来描述小数。 所谓“类型”&#xff0c;就是相似的数据所拥有的共同特征&#xff0c;编译器只有知道了数…

软件测试经典面试题,助你面试加分

Hi&#xff0c;大家好&#xff0c;进入金九银十&#xff0c;很多小伙伴有被动跳槽的打算&#xff0c;所以更新一些经典的软件测试面试题&#xff0c;希望能帮到大家&#xff01; 时间紧迫的情况下&#xff0c;如何做好测试工作&#xff1f; 对需求要明确&#xff0c;对需求的优…

iPhone如何全选删除照片:一步到位的清理指南

随着时间的推移&#xff0c;iPhone中的照片会迅速累积&#xff0c;最终可能占据大量的存储空间。无论是为了释放空间&#xff0c;还是整理照片库&#xff0c;iPhone如何全选删除照片成为许多用户的需求。然而&#xff0c;iPhone原生的“照片”应用并没有直接提供“全选删除”功…

汽车租赁管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图详细视频演示技术栈系统测试为什么选择我官方认证玩家&#xff0c;服务很多代码文档&#xff0c;百分百好评&#xff0c;战绩可查&#xff01;&#xff01;入职于互联网大厂&#xff0c;可以交流&#xff0c;共同进步。有保障的售后 代码参考数据库参…

SAIA触摸屏维修PCD7.D457VNCG03 SAIA-burgess

瑞士SAIA触摸屏维修SAIA-burgess思博控制器维修PCD7全系列。 触摸屏维修常见故障&#xff1a;黑屏、指示灯无任何显示&#xff0c;触摸屏上电无反应&#xff0c; 上电蓝屏、白屏&#xff0c;通电几分钟后屏幕变为蓝屏&#xff0c;主板故障&#xff0c;通讯时有时无&#xff0c…

LiveQing视频点播流媒体RTMP推流服务用户手册-概览:CPU使用、内存使用、在线人数、流量统计、带宽使用(Mbps)、存储使用、实时存储(MB/s)

LiveQing视频点播流媒体RTMP推流服务用户手册-概览:CPU使用、内存使用、在线人数、流量统计、带宽使用&#xff08;Mbps&#xff09;、存储使用、实时存储&#xff08;MB/s&#xff09; 1、概览1.1、CPU使用1.2、内存使用1.3、在线人数1.4、流量统计1.5、带宽使用(Mbps)1.6、存…

2024年系统集成企业数字化趋势与CRM研究报告

系统集成是一种新型的服务方式&#xff0c;是企业进行信息传递和共享的通用型智能工具&#xff0c;在企业系统优化升级、流程的打通和重构、数据的收集与分析应用&#xff0c;以及IT运维与安全保障等方面起着降低成本&#xff0c;提高效率的重要作用。 整体来看&#xff0c;近…

【乐吾乐大屏可视化组态编辑器】事件交互-场景交互

场景交互 在线使用&#xff1a;https://v.le5le.com/ 乐吾乐大屏可视化可以实现大屏页面与内嵌2d/3d场景相互通信&#xff0c;底层原理是利用了iframe通过postMessage发送消息。 下面以2d场景为例&#xff0c;实现步骤如下&#xff1a; 1. 首先配置场景2&#xff08;被嵌入…

【分享】格力手机色界G0245D 刷REC、root、 救砖、第三方rom教程和资源

开门见山 帮别人弄了一台 格力G0245D&#xff0c;把找到的资源和教程分享一下 教程 这个写的很详细了格力手机色界G0245D-Root-最简指南 不过教程里刷rec这一步漏了加上电源键&#xff0c;加上就行了。 附加参考&#xff1a;格力手机2刷机 格力手机二代刷机 GREE G0215D刷机…

STM32如何设置自动代码提示?

首先&#xff1a; 点击Window--->Preferences 进来之后在左上方输入keys&#xff0c;然后点击Keys&#xff0c;在Scheme下方那一栏中输入Content Assist 然后点击Content Assist--->在下方Binding栏中选择Tab--->Apply--->Apply and Close 设置完成&#xff0c;测…

hutool发邮件功能如何配置SMTP服务器参数?

hutool发邮件的教程指南&#xff1f;hutool发邮件性能优化方法&#xff1f; Hutool作为一个轻量级的Java工具库&#xff0c;其邮件发送功能因其简单易用而受到广泛关注。AokSend将详细介绍如何通过配置SMTP服务器参数来实现Hutool发邮件的功能。 hutool发邮件&#xff1a;优势…

为什么要用数字化营销管理平台?

数字化营销管理平台是一种利用数字技术来整合和优化营销流程的工具。它能够帮助企业更高效地进行市场推广、客户关系管理以及销售活动。 一、主要功能 1.数据整合与分析 整合多渠道数据&#xff0c;包括网站流量、社交媒体互动、电子邮件营销反馈等。通过数据分析&#xff0…

k8s高版本(1,28)部署NodePort模式下的ingress-nginx的详细过程及应用案例

文章目录 前言环境ingress安装应用案例(ingress-http案例&#xff1a; 基于名称的负载均衡) 前言 这个是nodeport模式下的&#xff0c;如果需要loadbalancer模式下的&#xff0c;看看博主下面以前的博客 链接: k8s学习–负载均衡器matelLB的详细解释与安装 链接: k8s学习–ing…