【数据结构】和栈一样简单的结构——队列

news2024/11/24 15:01:51

【数据结构】和栈一样简单的结构——队列

  • 一、前言
    • 1、什么是队列?
    • 2、使用什么结构实现?
  • 二、目标
  • 三、实现
    • 1、初始化工作
    • 2、入队
      • 2.1、图解思路
      • 2.2、代码实现
    • 3、出队
      • 3.1、图解思路
      • 3.2、代码实现
    • 4、打印队列(用于测试)
    • 5、返回队头元素
    • 6、返回队尾元素
    • 7、返回队列中元素个数
    • 8、判断队列是否为空
    • 9、销毁队列

一、前言

1、什么是队列?

对于队列,百度百科上对它的简介是这样的:

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。

其实队列放到我们生活中也非常好理解,比如我们平时在食堂打饭或在超市里买东西付款的时候都需要排队。我们排的队肯定是后面来的人要排到队尾,前面的人先被服务到,也就是遵循了先进先出规则:
在这里插入图片描述
其实队列的实现也是和栈一样简单的,因为它的插入和删除也是规定死了的只能头删和尾插,所以我们主要要实现的也只是两个接口而已。

2、使用什么结构实现?

对于队列的实现主要使用的是两种结构,数组和链表:
在这里插入图片描述
那么选择哪一种结构更加方便一点呢?这里能明确地告诉大家是使用链表实现更加简单。因为要是使用数组尾做队尾的话入队是挺方便的,但是出队的话就要将后面的元素全都向前移动一个位置,选用数组头做队尾则反过来出队也要移动数据。不管怎么样都是会有一端不太方便。

而使用链表来实现的话,我们可以定义两个指针head和tail分别记录链表的头和为,然后入队和出队就只需要执行头删和尾插即可,两个方法的复杂度都是O(1)。

所以我们就选用链表来实现队列。

二、目标

队列主要要实现的功能如下:

// 队列的入队
void QueuePush(Queue* pq, QDataType x);
// 队列的出队
void QueuePop(Queue* pq);
// 打印队列(用于测试)
void printQueue(Queue* pq);
// 返回队列的对头元素
QDataType QueueFront(Queue* pq);
// 返回队列的队尾元素
QDataType QueueBack(Queue* pq);
// 返回队列中的节点个数
int QueueSize(Queue* pq);
// 判断队列是否为空
bool QueueEmpty(Queue* pq);
// 销毁队列
void QueueDestroy(Queue* pq);

三、实现

1、初始化工作

老规矩,我们还是要先做好前期工作,将要用到的各种结构先定义一下:

// 重定义数据类型
typedef int QDataType;

// 定义节点类型
typedef struct QueueNode {
	struct QueueNode* next;
	QDataType data;
} QueueNode;

因为我们在队列中定义了两个指针head和tail,所以为了方便我们就再定义一个队列的类型,将这两个指针放到一个类型里,方便操作和维护:

// 定义队列类型
typedef struct Queue {
	QueueNode* head;
	QueueNode* tail;
} Queue;

然后就是对队列进行初始化,我们先将head和tail置空:

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

2、入队

2.1、图解思路

因为我们选择的是使用单链表来实现队列,所以入队的操作就基本和单链表的尾插一样了。又因为我们记录了链表的尾,所以我们就不在需要在重头找尾了,直接创建一个新节点,然后将其连接在tail的后面即可:
在这里插入图片描述
而仅有的特殊情况就是当我们的队列为空时候,我们需要将入head指针和tail指针同时指向这个入队的新节点:
在这里插入图片描述

2.2、代码实现

// 队列的入队
void QueuePush(Queue* pq, QDataType x) {
	assert(pq);
	// 创建一个新节点
	QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
	if (NULL == newNode) {
		perror("malloc fail!\n");
		exit(-1);
	}
	newNode->data = x;
	if (NULL == pq->head) {
		pq->head = newNode;
		pq->tail = newNode;
		pq->tail->next = NULL;
	}
	else {
		pq->tail->next = newNode;
		pq->tail = pq->tail->next;
		pq->tail->next = NULL;
	}
}

3、出队

3.1、图解思路

出队对应的就是单链表的头删,其操作和单链表的头删一样,我们先用一个next指针保存队头的下一个节点,然后再释放队头节点,最后再将head指向next即可:
在这里插入图片描述
然后还有一个特殊情况就是当出队出到队列为空的时候,根据上面的逻辑,头指针head是已经为空的了。但我们发现尾指针tail还没被处理,也就是说尾指针还指向这已被释放表的节点,这就会导致野指针了:
在这里插入图片描述

所以当我们把队列删空的时候也要将尾指针tail给置空:
在这里插入图片描述

3.2、代码实现

// 队列的出队
void QueuePop(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	QueueNode* next = pq->head->next;
	free(pq->head);
	pq->head = next;
	// 如果对头为空了,我们也要把队尾也给置空,避免野指针
	if (NULL == pq->head) {
		pq->tail = NULL;
	}
}

4、打印队列(用于测试)

然后我们再写一个用于测试的打印函数,这其实就是在打印链表啦:

// 打印队列
void printQueue(Queue* pq) {
	assert(pq);
	QueueNode* cur = pq->head;
	while (cur) {
		printf("[%d]——", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

打印和测试效果如下:
在这里插入图片描述

5、返回队头元素

这个其实就不用多说,如果队列不为空就直接返回即可:

// 返回队列的对头元素
QDataType QueueFront(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

6、返回队尾元素

这个也是一样:

// 返回队列的队尾元素
QDataType QueueBack(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

7、返回队列中元素个数

对于队列中的元素个数,其实我们可以在定义队列类型的时候再额外定义一个size成员来记录当前队列中的元素个数,然后在我们执行入队和出队的时候相应的让size自加1或自减1。这样能省事不少:

// 定义队列类型
typedef struct Queue {
	QueueNode* head;
	QueueNode* tail;
	int size;
} Queue;

但是size这个属性好像在大多数情况下都用不到,所以若是单独再定义一个size好像有点怪怪的又好像有点多余,所以我们干脆就额外写一个函数来返回队列中的元素个数。
这个逻辑其实也很简单,就是遍历链表而已:

// 返回队列中的节点个数
int QueueSize(Queue* pq) {
	assert(pq);
	assert(!QueueEmpty(pq));
	QueueNode* cur = pq->head;
	int size = 0;
	while (cur) {
		size++;
		cur = cur->next;
	}
	return size;
}

8、判断队列是否为空

这个函数我们直接返回head是否等于空的判断结果即可:

// 判断队列是否为空
bool QueueEmpty(Queue* pq) {
	assert(pq);
	return pq->head == NULL;
}

9、销毁队列

销毁队列其实也就是销毁链表,这其实和销毁链表的逻辑是一样的,但我们最后还有注意也要讲head和tail给置空了:

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

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

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

相关文章

Android工程师复盘小米、滴滴的面试全过程,网易offer已收入囊中

背景 时间过的真快,16年毕业到现在也有好几年了,最近面试试着投了几十家Android framework工程师的岗位,也面试了好几家,其中包括滴滴出行、小米、合众新能源、网易、最终收到了网易和滴滴offer,小米二面挂掉&#xf…

四、Eureka注册中心集群配置

目录 需要两个eureka server项目,之前已经有一个springcloud-eureka 1、在springcloud项目下新建一个聚合项目springcloud-eureka2 2、修改springcloud-eureka2的pom文件,引入eureka-server依赖 3、增加springcloud-eureka2的启动类,开启…

五、基于服务发现获取并访问远程接口

目录 1、在springcloud-order项目中新建controller供外部远程访问 2、在springcloud-member项目中新建controller去访问远程接口 3、运行springcloud-eureka、springcloud-member、springcloud-order项目的启动类 4、访问member服务的接口,通过member服务调用or…

WiFi(Wireless Fidelity)基础(十一)

目录 一、基本介绍(Introduction) 二、进化发展(Evolution) 三、PHY帧((PHY Frame ) 四、MAC帧(MAC Frame ) 五、协议(Protocol) 六、安全&#x…

python进阶--月考二

python进阶--月考二 (一)装饰器(二)创建名为express.py文件,编写以下推导式(25分)(三)创建名为process_test.py的文件,计算1-3000之间的水仙花数(…

xormplus是xorm的增强版,为xorm提供类似ibatis的配置文件及动态SQL支持

简介 xorm是一个简单而强大的Go语言ORM库,通过它可以使数据库操作非常简便。本库是基于原版xorm的定制增强版本,为xorm提供类似ibatis的配置文件及动态SQL支持,支持AcitveRecord操作。 github地址:https://github.com/armingli/xorm //安装…

荷兰国旗问题与快速排序

实现: 当arr[i]小于等于num时,arr[i]和小于等于num区域下一个数进行交换,小于等于区域右扩一个位置,指针 i 指向下一个 当arr[i]大于num时,指针 i 指向下一个 指针 i 越界时完成 升级版本:将小于、等于、…

存在comsumer group且存在消费行为,但AdminClient获取不到消费offset值

AdminClient 中的listConsumerGroupOffsets获取不到consumergroup; 网上查找资料说: listConsumerGroupOffsets() 请求返回的消费位移信息未及时更新,仍是稍早时刻的状态。 由于 listConsumerGroupOffsets() 请求需要从 Broker 获取最新的位移信息,如果响应结果的更…

ABAP SAT使用说明

SAT简介 SAT是SAP提供的用来替代SE30的程序性能优化分析工具( runtime analysis),功能比SE30更加强大。 创建SAT变式-设置SAT运行属性,默认为default,可以参照创建一个自己的变式。 SAT变式说明 Size Limits: 设置文件最大容量…

【LeetCode: 279. 完全平方数 | 暴力递归=>记忆化搜索=>动态规划 | 背包模型】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

Goby 漏洞更新 |商混ERP系统 DictionaryEdit.aspx 页面存在SQL注入

漏洞名称:商混ERP系统 DictionaryEdit.aspx 页面存在SQL注入 English Name:SQL injection exists on Lotus ERP DictionaryEdit.aspx pag CVSS core: 8.5 影响资产数:616 漏洞描述: 杭州荷花软件有限公司开发的商混ERP系统。…

开发板和虚拟机socket报错“connect error: No route to host”

学习socket编程时,将服务器程序运行在开发板上,将客户端应用程序运行在 虚拟机Ubuntu16.04 系统,服务器可以正常开启, 但客户端连接时, 报错“connect error: No route to host” 1、网上大多是以下情况: …

易智编译EaseEditing:计算机顶会不一定比SCI期刊更权威!

计算机SCI期刊和学术会议都是评估学术研究水平和影响力的重要指标,但在不同领域、不同学科、不同评价指标下可能会有不同的权威性。 在计算机科学领域,传统上认为顶级学术会议的影响力和权威性更高。 因为计算机科学发展较快,研究领域广泛&a…

【Linux】8. 环境变量

1. 环境变量的引入 先描述一个现象,我们在执行二进制可执行程序的时候,是需要找到其所在位置的(程序要运行必须先加载到内存,是因为冯诺依曼体系结构规定CPU只能从内存中读取数据),所以这也就是为什么我们在运行前带上./的原因&a…

洛谷P5717-三角形分类

洛谷P5717-三角形分类 题目 这道题更像是初中题,但是怎么能完整的按照题目的意思来解决呢,说实话这个题卡了我有一会儿,要做一次性做出这个题,我觉得需要搞清楚if-if 和if-else if-else if,试想这两个的区别是什么&am…

ChatGPT有意识吗?

​ 编辑切换为居中 添加图片注释,不超过 140 字(可选) ChatGPT是一个计算机程序,它没有意识。它只是根据预设的算法和规则来处理输入和输出。虽然它可以模拟人类的对话,但它没有自己的思想或感觉。它只是根据程序设…

ROS 下 激光扫描仪 YDLidar-G4 使用

环境配置: ubuntu20.04 LTS ROS noetic 编程工具:vs code,远程通过ssh访问 扫描仪:YDLidar-G4 YDLidar驱动: YDLidar SDK YDLidar ROS 功能包 1 . YDLidar-SDK通信协议 雷达扫描输出的数据以十六进制格式输出到通信…

Linux文本之sed流编辑器

一、sed的相关知识及其工作流程 1)sed编辑器的介绍 sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓…

使用HDFS底层文件进行HBase跨集群数据迁移

目录 一、概述 二、环境信息 三、HBCK2下载和编译 四、具体操作步骤 4.1 数据同步 4.2 添加元数据 4.3 重新分配region 一、概述 客户集群机房迁移,我们部署的集群也要完成跨集群迁移hbase 表,这里选择迁移Hadoop底层数据来实现hbase的表迁移。 …