【数据结构】队列详解

本篇要分享的内容是队列的解析和增删查改的使用,以下为本篇目录

目录

1.队列的概念及结构

2.队列的结构

3.队列的初始化

4.队列空间释放

5.入队

6.出队

7.获取队头和队尾元素

获取对头

获取队尾

8.计算队列元素

9.判空

11.本篇所有代码展示

Queue.c

Queue.h 

test.c


1.队列的概念及结构

队列的结构和栈完全相反,栈只能再栈顶进再从栈顶出,遵从后进先出(LIFO);而队列

只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,遵循的是先进先出,后进后出。

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

出队列:进行删除操作的一端称为队头,队列的图示如下

 那这里有一个小问题,如果入队列的顺序是1,2,3,4,那出队列的顺序也是1,2,3,4吗?

是的,出队列的顺序一定是1,2,3,4,并且无论在什么情况下都是1,2,3,4,永远都能保持先进先出,所以抽号机的原理就是用队列来实现的。

当然队列也分数组队列和链式队列,在这里我们研究链式队列是会对实际操作更方便一些的,因为队列需要操作的是队头和队尾,所以就要涉及到头部删除和尾部插入,而链表的插入和删除相对较容易一些,所以我们选择研究链式队列。

2.队列的结构

 既然是链式队列,并且只能头出尾进

那自然就会想到单链表的结构,所以我们可以先定义一个单链表的结构

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

但是仅仅这一个结构体是不够的,为什么呢?

我们要从测试的角度来看

int main()
{
	QNode* phead = NULL;
	QNode* tail = NULL;
	int size = 0;
	return 0;

}

首先我们需要定义两个结构体,从命名上不难看出一个指向队头,一个指向队尾,当然也可以定义size来测量队列的大小。

如果我的队列中要存放多个数据该如何操作呢?

单链表中的每个结点都有两个部分组成:一个Data和一个next,

我们不妨在定义一个结构体,将整个队列的队头和队尾都记录下来,代码如下

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

那这串代码就记录的是整个队列的头和尾了

 结合图示来看就是这个样子,应该不难理解。

3.队列的初始化

初始化是非常简单的,只需要将队列内置空即可

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

4.队列空间释放

释放队列空间也非常简单,因为是单链表式的结构所以要一个一个的释放空间,代码如下

void QueueDestory(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;
}

首先用asset来断言整个链表是否为空;

然后使用结构体指针cur来保存phead的位置,然后操作cur;

当cur不为空的时候就用结构体指针再定义一个next用来保存cur的next;

然后释放掉当前的cur的空间,再用next赋给cur,这样next就成为了新的cur,完成迭代;

当然如果cur为空时,那么整个队列也为空(QNode* cur = pq->phead),所以phead和ptail就可以直接置空,size也为0;

队列的置空和单链表的置空并没有太大差异,如果忘记了操作可以复习单链表销毁空间的操作。

5.入队

和单链表尾插大差不差,同样要判断链表中是否为空,这里不仅要判断队头还要判断队尾

void QueuePush(Queue* pq, QDataType x)
{
	
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;


    //只有一个结点的情况
	if (pq->ptail == NULL)
	{
		assert(pq->phead==NULL);
		pq->phead = pq->ptail = newnode;

	}
    
    //多个节点
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

入队和单链表中的尾插操作大差不差,所以要先找尾,然后malloc一段新的空间出来再插入数据、并且成为新的尾结点;

需要注意的时phead和ptail都需要判断,这样才能知道哪个结点是空;

所以还需要分成一个结点和多个结点的情况来讨论。

6.出队

出队和单链表的头部删除操作相似,所以还是要分一个结点和多个结点的情况来讨论。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//一个节点
	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--;
}

当只有一个结点时我们需要判断phead的next是否为空,也就是第二个结点是否为空,如果第二个结点没有数据那就为空,所以直接free掉第一个结点(pq->next)即可。

如果有多个结点的话,那就再定义一个新的结点,来保存当前节点的下一个结点

再把当前节点的空间free掉;

再将新的结点赋值给phead使其成为新的头节点;

最后要给队列中元素的计数size--;

7.获取队头和队尾元素

获取元素类似于查找函数,所以要注意函数的返回值是最开始typedef好的类型;

代码也非常简单,只需要用结构体访问数据内容即可;

获取对头

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

相信代码不难看懂;

8.计算队列元素

只需用结构体访问size即可

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

9.判空

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL;
}

思路也非常简单,根据图标来看判断头节点为空即可。

也可以使用size来看

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

当然如果是要用size来判空的话入队和出队的代码一定要记录好size,不要忘记给size的自加和自减。10.队列元素展现

和栈的展现相似,我们需要边出队边将其内容展现,以下是测试代码

#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);
	}
	printf("\n");


	QueueDestory(&q);
	return 0;

}

可以看到这里和队列展现的方式是相同的,需要访问一个弹出一个,运行结果如下。

 

当我们中途将数据弹出会怎样呢?

结果是一样的,因为队列的入栈顺序规定的就是先入先出。

11.本篇所有代码展示

Queue.c

#include"Queue.h"
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	/*return pq->phead == NULL;*/
	return pq->size == 0;
}


void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}
void QueueDestory(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->data = x;
	newnode->next = NULL;
	if (pq->ptail == NULL)
	{
		assert(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->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(!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);
	assert(!QueueEmpty(pq)); 
	return pq->size;
}

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 data;
}QNode;

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

void QueueInit(Queue* pq);
void QueueDestory(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);

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");


	QueueDestory(&q);
	return 0;

}

以上就是本篇所要分享关于队列的增删查改的所有内容,如果对你有所帮助还请三连支持,感谢您的阅读。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一、尚医通手机登录

文章目录 一、登录需求1、登录效果2、登录需求 二、登录1&#xff0c;搭建service-user模块1.1 搭建service-user模块1.2 修改配置1.3 启动类1.4 配置网关 2、添加用户基础类2.1 添加model2.2 添加Mapper2.3 添加service接口及实现类2.4 添加controller 3、登录api接口3.1 添加…

FPGA——HLS入门-LED闪烁仿真

系列文章目录 文章目录 系列文章目录一、HLS介绍1、什么是HLS2、与VHDL/Verilog有什么关系?3、关键技术局限性 二、Vivado HLS - LED闪烁仿真1、项目配置2、C仿真3、联合仿真 三、总结 一、HLS介绍 1、什么是HLS HLS就是高综合&#xff08;High level Synthesis&#xff09;…

公网远程访问本地jupyter notebook服务 - 内网穿透

文章目录 前言视频教程1. Python环境安装2. Jupyter 安装3. 启动Jupyter Notebook4. 远程访问4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5. 固定公网地址 转载自cpolar的文章&#xff1a;公网远程访问Jupyter Notebook【Cpolar内网穿透】 前言 Jupyter Notebook&am…

最优化方法Python计算:一元函数搜索算法——牛顿法

设函数 f ( x ) f(x) f(x)&#xff0c;在 [ a , b ] [a,b] [a,b]上二阶连续可微且有唯一的最小值点 x 0 x_0 x0​。由于 f ( x ) f(x) f(x)是 [ a , b ] [a,b] [a,b]上的单峰函数&#xff0c;故 f ′ ′ ( x ) > 0 f(x)>0 f′′(x)>0&#xff0c; x ∈ ( a , b ) x\in…

MyBatis快速入门

目录 一、什么是MyBatis 二、MyBatis的学习要领 三、搭建第一个MyBatis 3.1 创建数据库和表 3.2 添加MyBatis框架支持 3.2.1 老项目添加MyBatis 3.2.2 新项目去添加MyBatis 3.3 设置MyBatis配置信息 3.3.1 设置数据库连接的相关信息 3.3.2 设置MyBatis xml保存路径 和…

vue-cli4+vant+rem+sass+vuex+axios封装+webpack搭建前端项目

移动端项目模板 基于 vue-cli4.0 webpack 4 vant ui sass rem 适配方案axios 封装&#xff0c;构建手机端模板脚手架 启动项目 git clone https://github.com/teach-tian/h5-vue-cli4.gitcd h5-vue-cli4npm installnpm run serve✅ 配置多环境变量 package.json 里的 s…

SpringBoot【开发实用篇】---- 整合第三方技术(监控)

SpringBoot【开发实用篇】---- 整合第三方技术&#xff08;监控&#xff09; 1. 监控的意义2. 可视化监控平台3. 监控原理 在说监控之前&#xff0c;需要回顾一下软件业的发展史。最早的软件完成一些非常简单的功能&#xff0c;代码不多&#xff0c;错误也少。随着软件功能的逐…

Linux基于Apache服务搭建简易镜像站

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Linux基于Apache服务搭建简易镜像站 安装Apache服务器 yum install -y httpd.x86_64 配置Apache服务器&#xff1a;编辑Apache配置文件/etc/httpd/conf/httpd.conf #S…

ospf的rip和ospf互通以及配置stub区域和totally stub

1. ospf与rip如何互通 我们需要在两台路由器上互相引入,如上图 AR5和AR6运行了rip,但AR5也运行了ospf要想路由器能够互相学习到路由,就需要在AR5上配置路由协议引入 什么是stub区域如何配置stub区域 Stub区域的功能&#xff1a;过滤4类LSA和5类LSA&#xff0c;对外产生缺省的…

Unity之使用Photon Server + PUN2 开发局域网多人游戏

一.前言 Photon Engine是一款跨平台的实时多人游戏引擎,它提供了可靠的基础设施和工具,使开发者能够轻松地构建和部署多人游戏。Photon Engine支持多种平台,包括PC、移动设备和Web,同时还提供了多种语言的SDK,如C++、C#、Java、JavaScript等,使得开发者可以使用自己熟悉…

宁德时代,冷暖自知口难言

作者 | 魏启扬 来源 | 洞见新研社 发布可以“上天”的凝聚态电池、落地能量密度160Wh/kg的钠离子电池、量产系统集成度全球最高的麒麟电池…… 宁德时代在上海车展前后密集发声&#xff0c;坚决捍卫着“宁王”的冠冕。 如果再结合不久前的2022年年报&#xff0c;全年307亿的…

条码控件Aspose Barcode,满足您条码需求的终极解决方案

Aspose.BarCode for .NET 是一个功能强大的API&#xff0c;可以从任意角度生成和识别多种图像类型的一维和二维条形码。开发人员可以轻松添加条形码生成和识别功能&#xff0c;以及在.NET应用程序中将生成的条形码导出为高质量的图像格式。 Aspose API 支持流行文件格式处理&a…

如何在 Ubuntu 22.04 上安装 Python Pip?

Python Pip 是 Python 的包管理器&#xff0c;它允许您轻松地安装和管理 Python 包和库。在 Ubuntu 22.04 上安装 Python Pip 是非常简单的。 本文将详细介绍如何在 Ubuntu 22.04 上安装 Python Pip&#xff0c;并为您提供逐步指南。 步骤 1&#xff1a;更新软件包列表 在安装…

C嘎嘎~~[谈谈C++的一些优化]

C的一些优化 匿名对象引用引用作形参引用作返回值 编译器优化构造 拷贝构造 ⇒ 构造拷贝构造 拷贝构造 ⇒ 一个拷贝构造 匿名对象 通过以前C语言的学习, 我们知道了有一种 具有临时性的, 没有名字的变量 — — 匿名变量. 那么我们的对象应该也有这个特性 — — 匿名对象 匿名…

Kotlin 协程中的并发问题:我明明用 mutex 上锁了,为什么没有用?

前言 最近在接手的某项目中&#xff0c;主管给我发来了一个遗留以久的 BUG&#xff0c;让我看看排查一下&#xff0c;把它修复了。 项目的问题大概是在某项业务中&#xff0c;需要向数据库插入数据&#xff0c;而且需要保证同种类型的数据只被插入一次&#xff0c;但是现在却…

day15 - 使用图像金字塔进行图像拼接

在我们之前的学习过程中&#xff0c;使用的都是恒定大小的图像&#xff0c;但是在某些情况下&#xff0c;我们需要使用不同分辨率的&#xff08;相同&#xff09;图像。例如&#xff0c;当在图像中搜索某些东西&#xff08;例如人脸&#xff09;时&#xff0c;我们不确定对象将…

【高级语言程序设计(一)】第 10 章:文件

目录 一、文件概述 &#xff08;1&#xff09;文件定义 &#xff08;2&#xff09;文件命名 &#xff08;3&#xff09;文件分类 ① 按照文件的内容划分 ② 按照文件的组织形式划分 ③ 按照文件的存储形式划分 ④ 按照文件的存储介质划分 &#xff08;4&#xff09;文…

系统集成项目管理工程师 下午 真题 及考点(2019年上半年)

文章目录 一&#xff1a;第10章 项目质量管理&#xff0c;规划质量管理输出&#xff0c;质量成本法&#xff08;一致性成本【预防、评价】 和 非一致性成本【内部失败、外部失败】&#xff09;&#xff0c;七种工具二&#xff1a;第8章 项目进度管理&#xff0c;总浮动时间&…

26 VueComponent 其他属性的更新

前言 这是最近的碰到的那个 和响应式相关的问题 特定的操作之后响应式对象不“响应“了 引起的一系列的文章 主要记录的是 vue 的相关实现机制 呵呵 理解本文需要 vue 的使用基础, js 的使用基础 测试用例 比如这里看一下 class 的更新 测试用例如下, 增加 topClazz …

4、js - 闭包

1、闭包的概念 闭包&#xff1a;函数嵌套函数&#xff0c;内层函数访问了外层函数的局部变量。 // 闭包 function func1() {let a 9;let b 8;function func2() {console.log("a", a); // a 9}func2(); } func1(); 分析&#xff1a; 需要访问的变量会被放到闭包…