C数据结构:队列

news2024/10/6 1:44:04

目录

队列是什么?

队列的实现

队列的数据结构

队列的初始化

队列的插入

队列的删除

获取队列队头元素

获取队列队尾元素

获取队列元素个数

检查队列是否为空

队列的销毁

队列的使用

完整代码


队列是什么?

队列也是顺序表中的一种

队列和栈差不多,它们的插入和删除都是限定在特定位置的

栈是后进先出,队列则是先进先出

栈只允许在队尾入数据,这叫入队

栈只允许在队头出数据,这叫出队

队列的实现

队列这种特殊的结构既可以通过数组的方式实现,也可以链表的方式来实现,这时候就要考虑它两的优缺点来决定使用谁来实现了

根据队列的性质我们需要频繁的在队头和队尾插入删除数据,如果是使用数组来实现的话如果只是在数组队尾插入还很容易,但是如果在队头删除数据就需要整体往后挪一位,这样做效率是非常低下的

而如果是使用链表来实现对于插入删除并且是在头尾来说是非常easy的,所以这里队列的实现是使用的单链表实现

为什么不用双向链表?

双向链表也很好,但是我们需要多定义一个prev指针,我们在出队和入队的过程中并不是很需要这个prev指针(如果只是为了出队方便完全可以定义一个队头节点的指针一直指向队头即可), 如果多了一个prev指针那么就意味着我们需要多维护一个指针变量,并且还会多消耗一点空间

所以,相较来说使用单链表就足以轻松的完成队列的实现

链表不熟悉的可能需要先看看链表,可以看看下面的链接 

C数据结构:单链表-CSDN博客 

下面就开始使用单链表实现吧

队列的数据结构

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

如何来理解这个代码呢?请看下图 

 

先看第一个struct

在这个QNode中有两个成员,一个是自己结构体的指针,一个是QDataType类型的val(这里的QDataType为int)

指针也就对应了图里这3个QNode指向对方的箭头,val就是里面的值

这个QNode是必须要定义的,这是链表的基础,为了完成我们的队列而创建的,这是我们底层的结构

而第二个struct

里面有两个QNode的指针,一个指向头,一个指向尾

定义Queue结构体的原因是:方便后续的插入和删除操作,现在定义一个size也是方便后续的函数能直接获取元素个数,到时候在主函数直接定义一个Queue的变量即可完成队列的操作

队列的初始化

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

初始化时队列里是一个节点都没有的,所以phead和ptail就为NULL,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->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}

	pq->size++;
}

队列的插入的第一步我们需要先创建一个QNode的节点

这里先定义了一个QNode指针newnode,并malloc开出一块充足的空间 

下面的 if 语句就是每次动态扩容开辟完成后需要进行的检查扩容是否成功

若newnode为NULL,则扩容失败,直接返回

扩容失败的情况很少,一般正常扩容不会失败,若开的空间过大则可能失败

若扩容成功,那么我们需要对刚开出来的空间初始化,next=NULL,val=x

有了节点就该进行插入操作了

插入需要分两种情况,一种是第一次插入,还没有链表的时候,那么phead和ptai都指向newnode即可

否则,直接在ptai的后面插入即可,不要忘了让ptai往前走,ptai需要一直指向尾

最后size++即可

队列的删除

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

	pq->size--;
}

 队列的删除也分两个情况

一种是只有一个节点的时候

这里使用了phead==ptail时则为只有一个节点,当然这并不是唯一的判断方法

也可以这样:phead->next == NULL

若只有一个节点,直接释放掉phead指向的那块空间即可

注意这里只能free(phead)或者free(ptail),不能两个同时进行,因为它两指向的都是同一块空间,free释放的并不是这个指针而是指针指向的空间,都释放就会报错

 释放完后不要忘了让phead和ptail置NULL,防止变成野指针

若有多个节点

只需要先记住next位置,然后释放掉phead指向的空间,最后让phead走到next位置即可

最后不要忘了size--

获取队列队头元素

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

	return pq->phead->val;
}

队头元素的获取就非常容易了

只需要返回phead指向的元素即可 

获取队列队尾元素

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

	return pq->ptail->val;
}

和队头的获取一样

只需要返回ptail指向的元素即可

获取队列元素个数

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

这时候结构体里加的一个size成员的作用就体现出来了

如果没有size成员那么我们需要遍历一遍整个链表才能获取到数据,若我们需要多次调用这个QueueSize函数并且链表还长的话,我们就会浪费很多的时间在遍历链表上 

所以这里直接返回size成员即可 

检查队列是否为空

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

若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;
}

队列的销毁其实就和链表的销一致

定义一个cur遍历链表的同时释放掉当前的空间即可

最后也不要忘了将phead和ptai置NULL,防止变成野指针,size = 0

这样链表的实现就完成了

队列的使用

队列的使用和前面栈的使用思想差不多

int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return 0;
}

当前main函数先定义了一个Queue的变量q

先是需要对q进行初始化,调用初始化函数

然后向队列中向队头插入了4个元素:1,2,3,4

当队列不为空时

打印队头的数据,打印后删除队头的数据

这样就完成了在队尾插入数据,在队头删除数据,所以队列是先进先出

最后不要忘了销毁队列,防止内存泄漏

完整代码

Queue.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.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

#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->phead == 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);
	
	//一个节点
	if (pq->phead == pq->ptail)
	{
		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);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return 0;
}

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

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

相关文章

Python-VBA函数之旅-staticmethod函数

目录 一、staticmethod函数的常见应用场景 二、staticmethod函数使用注意事项 三、如何用好staticmethod函数&#xff1f; 1、staticmethod函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://blog…

Linux(Ubuntu24.04) 安装 MinIO

本文所使用的 Ubuntu 系统版本是 Ubuntu 24.04 ! # 1、下载 MinIO wget https://dl.min.io/server/minio/release/linux-amd64/minio# 2、添加可执行权限 chmod x minio# 3、导出环境变量&#xff0c;用于设置账号密码&#xff0c;我设置的账号和密码都是 minioadmin export MI…

【数据结构练习题】Map与Set——1.只出过一次的数字2.复制带随机指针的链表3.宝石与石头4.坏键盘打字

♥♥♥♥♥个人主页♥♥♥♥♥ ♥♥♥♥♥数据结构练习题总结专栏♥♥♥♥♥ ♥♥♥♥♥【数据结构练习题】堆——top-k问题♥♥♥♥♥ 文章目录 1.只出过一次的数字1.1问题描述1.2思路分析1.3绘图分析1.4代码实现2.复制带随机指针的链表2.1问题描述2.2思路分析2.3绘图分析2.4代…

Android解放双手的利器之ViewBinding

文章目录 1. 背景2. ViewBinding是什么3. 开启ViewBinding功能4. 生成绑定类5. 使用ViewBinding5.1Activity 中使用5.2 Fragment 中使用5.3 ViewHolder 中使用 6. ViewBinding的优点7. 与 dataBinding 对比 1. 背景 写代码最繁琐的是什么&#xff1f;重复的机械操作。我们刚接…

【AI+老照片焕新】母亲节用AI把时间的印记变成暖心礼物

想念是一张泛黄的照片&#xff0c;藏在抽屉里的笑容&#xff0c;总是那么亲切。今天是母亲节&#xff0c;是不是想给妈妈来点不一样的惊喜&#xff1f;用AI技术&#xff0c;把那些老照片瞬间焕新&#xff0c;让妈妈的青春记忆重放光华&#xff01; 想象一下&#xff0c;妈妈年…

vue3vue3vue3vue3vue3vue3vue3vue3vue3vue3vue3vue3

纯vue3的语法 一.创建&#xff08;基于vite&#xff09; 1.在指定目录下运行 npm create vuelatest 项目名称&#xff1a;英文小写下划线数字回车表示确定是、否 左右切换路由、pina、单元测试、端到端的测试、开启eslint控制代码质量 先选择no&#xff0c;学的时候自己手动…

4---自动化构建代码(逻辑梳理,轻松理解)

一、需求引出&#xff1a; 在使用编译器编译代码时&#xff0c;无论我们在一个项目中写了多少个文件(包括头文件、源文件)&#xff0c;我们都可以一键完成编译&#xff0c;编译器会自动处理各个文件之间的包含&#xff0c;调用关系。但是在Linux中&#xff0c;我们在一个目录下…

Docker in Docker(DinD)原理与实战

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Docker幻想曲&#xff1a;从零开始&#xff0c;征服容器宇宙》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、Docker简介 2、Docker …

Kubernetes学习-深入Pod篇(一) 创建Pod,Pod配置文件详解

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Kubernetes渐进式学习-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 1.前言 我们在前面的文章讲解了Kubernetes的核心概念和服务部署&#x…

08.3.grafana自定义图形

grafana自定义图形 找插件里面的zabbix 点击update 数据源—zabbix数据源,添加zabbix数据源 选择zabbix类型 我这里配置的是本地&#xff0c;所以URL直接localhost 这里配置zabbix登录账号密码Admin/zabbix 然后点击保存并测试&#xff0c;会直接显示版本 导入模板&…

JavaSE——集合框架一(1/7)-集合体系概述(集合体系结构,Collection集合体系)、Collection的常用方法(介绍,实例演示,代码)

目录 集合体系概述 集合体系结构 Collection集合体系 Collection的常用方法 介绍 实例演示 完整代码 集合体系概述 集合体系结构 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组&#xff0c;但集合的大小可变&#xff0c;开发中也非常常用。 为了满足…

ACM 的代码编码示例

写在最前面的 实践的顺序&#xff0c; 应该是先将基础的 数据结构题目类型给实现。 然后再开始尝试 实现对应类型的算法题目&#xff0c;如回溯算法&#xff0c; 贪心算法&#xff0c; 动态规划&#xff0c; 图论&#xff1b; 基础的数据结构&#xff0c; 推荐卡尔的&#xff…

【C++】vector的底层原理讲解及其实现

目录 一、认识vector底层结构 二、初始化vector的函数 构造函数拷贝构造赋值构造initializer_list构造迭代器区间构造 三、迭代器 四、数据的访问 五、容量相关的函数 六、关于数据的增删查改操作 一、认识vector底层结构 STL库中实现vector其实是用三个指针来完成的&#x…

PY32F403系列单片机,32位M4内核MCU,主频最高144MHZ

PY32F403系列单片机是基于Arm Cortex-M4核的32位通用微控制器产品。内置的FPU和DSP功能支持浮点运算和全部DSP指令。通过平衡成本&#xff0c;性能&#xff0c;功耗来获得更好的用户体验。 PY32F403单片机典型工作频率可达144MHZ&#xff0c;内置高速存储器&#xff0c;丰富的…

sql注入之bool盲注

目录 盲注步骤 1、进入靶场 2、如下图所示输入&#xff1f;id1‘ 判断此时存在注入点 3、判断列数 ​编辑 4、开始盲注 普通的python脚本 代码思想 结果 二分查找python脚本 二分查找算法思想简介 二分查找与普通查找的主要差距 代码思想 代码 结果​编辑 下面以…

图像融合-下游任务(目标检测、实例分割、深度估计、局部区域细节放大)

下游任务: 采用目标检测、实例分割和深度估计的下游任务来验证图像融合结果质量。 文章目录 下游任务:1.目标检测2.实例分割3.深度估计局部细节放大工具Update1.目标检测 YOLOv8:https://github.com/ultralytics/ultralytics 步骤内容第一步下载项目到本地第二步安装READ…

文献阅读——中国农产品期货的正负价格泡沫(LPPLS)

Positive and negative price bubbles of Chinese agricultural commodity futures Fang, Ming, Yizhou Lin, and Chiu-Lan Chang. “Positive and negative price bubbles of Chinese agricultural commodity futures.” Economic Analysis and Policy 78 (2023): 456-471. 经…

以项目为中心打造企业卓越经营管理体系︱PMO大会

全国PMO专业人士年度盛会 广东林氏家居股份有限公司战略副总裁杨永柠先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“以项目为中心打造企业卓越经营管理体系”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&…

WPS表格:使用vlookup函数解决乱序数据对应问题

我们常常会遇到两个表格的内容相同&#xff0c;但是顺序不一致的情况。并且这种顺序无关于简单的排序&#xff0c;而是一种业务性很强的复杂排序规则。下面我举个例子&#xff0c;使用VLOOKUP复制数据。 假设太阳系行星举办了一次卖萌比赛&#xff0c;由太阳妈妈决定谁是最萌的…

【JVM】ASM开发

认识ASM ASM是一个Java字节码操纵框架&#xff0c;它能被用来动态生成类或者增强既有类的功能。 ASM可以直接产生二进制class文件&#xff0c;也可以在类被加载入虚拟机之前动态改变类行为&#xff0c;ASM从类文件中读入信息后能够改变类行为&#xff0c;分析类信息&#xff…