【数据结构】队列详解

news/2024/10/18 12:31:47/

前言

前面我们学习了一种数据结构:栈,栈是一种只允许在一端尽进行插入删除的数据结构,而今天我们将学习另一种数据结构:队列,队列是一种支持在一端进行插入,在另一端进行删除的数据结构。

一、队列的介绍

队列是一种支持在一端进行插入,在另一端进行删除的数据结构,相当于尾插和头删,入队列的一端我们称之为队尾,出队列的一端我们称之为对头。
在这里插入图片描述

  • 入队:向队列插入元素的操作,只能从队尾插入元素
  • 出队:对队列进行删除元素的操作,只能从队头删除元素
  • 基于队列上面的性质,队列的特点是先进先出的。这个需要和栈的后进先出区分开。

二、队列数据类型的重定义

在这里插入图片描述
与前面学习的数据结构一样,为了能够方便修改队列存储的数据类型,我们需要队数据类型进行重定义

三、队列的结构

因为队列中需要进行入队和出队,即尾插和头删,头删数组就不太方便了,因为数组的头删需要挪动后面的数据,效率比较低,所以采用链表的形式来实现队列。为了效率,我们需要准备两个指针来管理整个队列,因为我们需要对这个队列进行头删和尾插,所以需要两个指针分别标识队列的头结点和尾结点。实现链表的形式的队列,那么我们首先需要确定链表的结点的结构

  1. 队列中链表的结点的结构
    在这里插入图片描述
    和普通链表一样,队列中的链表同样需要存储数据,所以需要一个数据域,每一个结点需要找到其下一个结点,因此需要一个指针域指向每一个结点的下一个结点
    为了后面方便表示,我们同样可以对这个结点的结构进行重定义
    在这里插入图片描述
  2. 队列中的头指针和尾指针
    因为这是两个指针,所以我们可以考虑将这两个指针封装称为一个结构体,叫做队列Queue.也就是一个队列只需要知道其头结点和尾结点,那么我们就可以对这个队列进行操作了
    在这里插入图片描述
    同样的道理,为了后续方便表示,我们可以对这个结构进行重定义
    在这里插入图片描述

四、队列常见的基本操作

1. 声明

// 基本操作的声明、// 初始化
void QueueInit(Queue* pq);// 销毁队列
void QueueDestroy(Queue* pq);// 入队
void QueuePush(Queue* pq, QDataType val);// 出队
void QueuePop(Queue* pq);// 判空
bool QueueEmpty(Queue* pq);// 队头元素
QDataType QueueFront(Queue* pq);// 队尾元素
QDataType QueueBack(Queue* pq);// 队列结点个数
size_t QueueSize(Queue* pq);

在上面的函数声明中,我们发现函数的参数传的是队列结构体的地址,而不是结构体本身,道理和栈中的传参是一样的,首先可以节省空间,其次,我们需要在函数中通过这个队列的结构体指针找到队列的队头指针和队尾指针,如果传的是结构体,那么传参的过程是一次深拷贝,形参是实参的一份临时拷贝,这是两份不同的数据了,通过形参结构体找到的队头指针和队尾指针和实参的队头指针和队尾指针不是同一份数据,因此我们传的是队列的结构体指针。

2. 定义

  • 初始化
// 初始化
void QueueInit(Queue* pq)
{assert(pq);pq->head = pq->tail = NULL;
}

因为队列中有两个指针,所以我们需要对队列进行初始化,防止队列的两个指针变成野指针,初始化就是将队列的两个指针置成空指针

  • 销毁队列
// 销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->head;while (cur){QNode* next = cur->next;free(cur);cur = next;}pq->head = pq->tail = NULL;
}

这个思路和销毁链表的思路是一样的,需要遍历队列中的每一个结点,然后依次进行释放结点空间,最终将队列的头指针和尾指针置成空指针即可。

  • 入队
// 入队
void QueuePush(Queue* pq, QDataType val)
{assert(pq);// 申请新节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){printf("malloc fail\n");return;}newnode->val = val;newnode->next = NULL;// 入队if (pq->tail == NULL){// 第一次入队pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}}

入队的步骤,首先申请空间创建新节点,在入队的时候因为这个队列的链表是没有头结点的,所以在入队的时候需要判断是否为第一次入队,如果是第一次入队,则需要同时将头指针和尾指针指向这个结点,即第一个结点,如果不是第一次入队,则只需要将尾指针的下一个更新为新插入的元素(结点),然后更新尾指针即可。

  • 出队
// 出队
void QueuePop(Queue* pq)
{assert(pq);// 删除操作一定要判断是否为空assert(pq->head);// 删除结点// 先保存下一个位置QNode* next = pq->head->next;free(pq->head);pq->head = next;
}

出队的时候即删除元素的时候一定要检查队列是否为空,如果队列为空,是不能删除元素的,队列的删除元素的方式和链表删除元素的方式是一样的,先保存第二个结点,再删除第一个结点,再更新第二个结点为新的第一个结点。

  • 判空
// 判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}

当队列中的头指针为空的时候队列就是空的

  • 返回队头元素
// 队头元素
QDataType QueueFront(Queue* pq)
{assert(pq);assert(pq->head);return pq->head->val;
}
  • 返回队尾元素
// 队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);assert(pq->head);return pq->tail->val;
}

这两个函数接口需要注意判断队列是否为空,如果队列为空,则队列中没有元素,是不能返回队头元素和队尾元素的

  • 队列元素个数
int QueueSize(Queue* pq)
{if (pq->head == NULL){return 0;}int count = 1;QNode* cur = pq->head;while (cur!=pq->tail){cur = cur->next;count++;}return count;
}

注意此时count的初始值,当count初始值为0时,需要从头遍历到空,当count的初始值为1时,只需要遍历到尾结点即可。

五、遍历队列

  1. 代码
void test_queue5()
{// 遍历队列Queue q;// 初始化QueueInit(&q);// 入队QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);printf("此时队列的队头元素为:%d\n", QueueFront(&q));printf("此时队列的队尾元素为:%d\n", QueueBack(&q));printf("此时队列元素个数为:%d\n", QueueSize(&q));while (!QueueEmpty(&q)){printf("%d ", QueueFront(&q));QueuePop(&q);}printf("\n");printf("此时队列元素个数为:%d\n", QueueSize(&q));}
  1. 结果
    在这里插入图片描述
  2. 思路分析
    遍历队列需要一个循环来实现,当队列不为空时,先访问队头元素,再删除队头元素(入队),知道队列为空时退出循环。

http://www.ppmy.cn/news/11188.html

相关文章

Esp8266学习2. Node-mcu基于Arduino IDE2.0.3设置及基本操作

Esp8266学习2. Node-mcu基于Arduino IDE2.0.3设置及基本操作一、准备工作1. 下载Aruino IDE2. 准备Node-MCU开发板二、设置1. 填写开发板网址2. 开发板设置3. 连接开发板三、测试点亮LED程序1. 加载示例程序2. 编译运行四、一些基本网络操作1. 连接到热点2. 使用WiFiClient3. 创…

mysql的binlog学习记录

文章目录什么是binlogbinlog格式StatementRowMixedbinlog使用什么是binlog MySQL Binary Log也就是常说的bin-log, ,是mysql执行改动产生的二进制日志文件。简单的来说,binlog日志用于记录所有更新了数据或者以及潜在更新了数据(例如,没有匹…

webviz安装,docker安装可正常使用与Foxglove Studio

Foxglove Studio Foxglove Studio与webviz使用起来非常类似 去可以直接使用web也可以下载安装包 Foxglove Studio不提供源码 安装包下载地

初级开发者福音:手把手教你实现数字滚动效果~

文章目录一、前言二、背景知识三、实现方案Step 1:分析需求Step 2:实现单个数字的滚动效果Step 3:组件接口设计Step 4:完善组件一、前言 前端数字滚动显示的场景很多,比如抽奖的时候,营造一种马上公布中奖…

Linux操作系统--文件管理(保姆级教程)

文件系统类型的含义 文件系统类型式指文件在存储介质上存放及存储的组织方法和数据结构。 Linux采用虚拟文件系统技术(virtual file system)-VFS 一个世纪的文件系统想要被Linux支持,就必须提供一个符合VFS标准的接口,才能与VFS协同工作&am…

数学建模-回归分析(Stata)

注意:代码文件仅供参考,一定不要直接用于自己的数模论文中国赛对于论文的查重要求非常严格,代码雷同也算作抄袭 如何修改代码避免查重的方法:https://www.bilibili.com/video/av59423231 //清风数学建模 一、基础知识 1.简介 …

DB性能跟不上,加缓存就够了?

服务端软件开发时,通常会把数据存储在DB。而服务端系统遇到的第一个性能瓶颈,往往发生在访问DB时。 这时大部分开发会拿出“缓存”,通过使用Redis在DB前提供一层缓存数据,缓解DB压力,提升服务端性能。 在数据库前添加…

Oracle常用命令,DBA的基础操作,Oracle运维基础操作

用户 sys 和system都是系统管理员(DBA),拥有最大的权限,密码是安装时设置的; scott是普通用户,拥有一些用于学习的表,初始密码是tiger。 内置角色 角色是权限的集合,以下是三个内…