【数据结构取经之路】队列循环队列

news2024/9/23 20:12:19

目录

引言

队列的性质

队列的基本操作

初始化

判空

销毁

队列的长度

插入

删除

返回队头元素

循环队列

假溢出

空与满的判定

实现 

初始化

插入

判空

销毁

删除

返回队列长度

返回队列头元素

判满


引言

队列和栈一样,也是数据结构的一种,可以用数组实现,也可以用链表实现。它常用于各种应用程序中,包括操作系统、网络通信等。一个最典型的例子就是操作系统中的作业排队。在允许多道程序运行的计算机系统中,同时有几个作业运行。如果运行的结果多需要通过通道输出,那就要按请求输出的先后顺序排队。每当通道传输完毕可以接受新的输出任务时,队头的作业先从队列中退出作输出操作。

队列的性质

队列的性质与栈相反,它是一种“先进先出”的线性表。它只允许在队列的一端进行插入,在另一端进行删除。允许插入的一端叫队尾,允许删除的一端叫队头。

队列的基本操作

队列的操作和栈的操作类似,下面将以链式队列为例,逐一讲解各个操作。因为是用单链表实现队列,如果仅仅记录队头,那么在插入数据时必须得遍历一遍链表,找到队列的尾,才能链接上新的结点,这是一个不小的消耗,所以,除了对链表的结点封装成一个结构体(A)外,还可以再封装一个结构体(B),B结构体成员里包含结构体A。这么说很抽象,还是看代码吧!

typedef int QueueDataType;

typedef struct QueueNode
{
	QueueDataType data;
	struct QueueNode* next;
}QueueNode;

typedef struct Queue
{
	QueueNode* phead;//指向链表头
	QueueNode* ptail;//指向链表尾
	int size;//记录长度
}Queue;

这样就方便对队列进行维护了。

初始化

初始化时,可以malloc出一些空间给队列,也可以不用,等到插入时再开辟空间也行,都无可厚非,我这里选择后者。

代码:


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

判空

链表为空的标志是size == 0.

代码:

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

销毁

因为队列底层是用单链表实现,所以在销毁时得一个节点一个节点的释放。在销毁节点A前,需要先记录A的下一个结点,否则把A释放后,将找不到它的下一个节点。销毁链表时还有一个细节,当只有一个节点时,free头结点后,要将头指针和尾指针都置空,如果仅把头指针置空,那么尾指针就是野指针。

代码:

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

	while (pq->phead)
	{
		QueueNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
		if (next == NULL) pq->ptail = NULL;//只有一个节点时,防止ptail为野指针
	}
}

队列的长度

size就是队列的长度,返回size即可。

代码:

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

插入

由于定义了尾指针,所以在插入前,就不需要遍历链表找尾了。第一种情况,链表不为空,不需要调整头指针,直接将新节点链接到尾节点后面即可,但不要忘了size++。第二种情况,链表为空,插入时,头指针和尾指针的指向都需要修改。

代码:

//创建节点
QueueNode* BuyNode(QueueDataType x)
{
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	newnode->next = NULL;
	newnode->data = x;

	return newnode;
}

void QueuePush(Queue* pq, QueueDataType x)
{
	assert(pq);

	QueueNode* newnode = BuyNode(x);

	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;//更新尾指针
	}
	pq->size++;
}

删除

第一点,当队列为空时,无法删除。第二,队列的删除操作是在队头进行的,所以需要更新头指针的指向。最后,不要忘了size--。

代码:

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	QueueNode* newhead = pq->phead->next;
	free(pq->phead);
	pq->phead = newhead;
    pq->size--;
}

返回队头元素

通过头指针便可以访问到队头元素。当队列为空时,无法返回。

代码:

QueueDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

循环队列

循环队列是把顺序队列首尾相连,把存储队列元素的数组从逻辑上看成一个环,成为循环队列。在物理上,它就是个数组,在逻辑上,我们要把它想象成一个圆环。循环队列的实现一般定义两个指针,头指针front和尾指针rear,front和rear开始都赋为0,每插入一个元素,rear++,每删除一个元素,front++,这和顺序栈如出一辙。front指向头,rear指向尾元素的下一个位置。

循环队列遵循先进先出的原则。

从队尾入队列,从队头出队列。

用数组实现的循环队列支持下标的随机访问,访问速度快。 

假溢出

请看上图,往队列里插入8个元素使队列呈满的状态,接着删除两个元素,此时,队列里有两个空位,但这两个空位是无法使用的,因为rear已经越界了,无法再继续插入。这种现象就叫作“假溢出”。这就导致了空间的浪费。

克服假溢出的方法有两种,第一种,把数组元素往前挪动,使其起始位置从0开始,显然,这种方法是很浪费时间的。第二种方法,就是使用循环队列,当存到最后一个地址后,下一个存放的位置就是从0开始。这也使得用数组实现的循环队列长度是固定的,不能动态增长。 

空与满的判定

循环队列的循环效果是用取余运算来实现的。在实现循环队列之前,我们首先搞清楚下面的问题——如何区分循环队列的空与满。

对比图a和图d1,我们发现,当循环队列为空时,存在front == rear,当循环队列满时,也存在front == rear,这样,就导致无法区分循环队列的空和满。解决方案有两种,第一种,令设一个变量来记录队列中的元素个数,以区分空和满;第二种,留出一个元素的空间,当队列头指针在队列尾指针的下一位置时,队列为满,队列为空的标志是front == rear,这样就将空和满这两种情况给区分开了。 

实现 

#define MIXSIZE 10

typedef int CQueueDataType;

typedef struct CycleQueue
{
	CQueueDataType a[MIXSIZE];
	int front;
	int rear;
}CycleQueue;

初始化

代码:

void CycleQueueInit(CycleQueue* pcq)
{
	assert(pcq);
	pcq->front = 0;
	pcq->rear = 0;
}

插入

如果队列已满,那么将无法插入。因为循环队列是定长的,长度不可动态增长。往队列中插入数据时还有以下细节:

第一种情况,直接rear++即可。第二种情况,在插入数据以后,如果直接rear++,将会越界,需要做特殊处理。

以上两种情况的统一处理方式为:

rear = (rear + 1)% MIXSIZE; 

当rear为最后一个元素的下标时,rear + 1 就是MIXSIZE,再模上MIXSIZE,正好为0,回到了数组的起始位置。由于取模操作本质上是去掉整除的部分,所以当rear < MIXSIZE时,进行取模操作后也可以得到正确的结果。

代码:

void CycleQueuePush(CycleQueue* pcq, CQueueDataType x)
{
	assert(pcq);
	assert(!CycleQueueFull(pcq));

	pcq->a[pcq->rear] = x;
	pcq->rear = (pcq->rear + 1) % MIXSIZE;
}

判空

循环队列为空的标志是:front == rear。

代码:

bool CycleQueueEmtpy(CycleQueue* pcq)
{
	assert(pcq);
	return pcq->front == pcq->rear;
}

销毁

代码:

void CycleQueueDestroy(CycleQueue* pcq)
{
	assert(pcq);
	free(pcq->a);
	pcq->front = 0;
	pcq->rear = 0;
}

删除

由于循环队列是用数组实现,所谓的删除并不是抹除数据,而是通过控制下标,让该元素在删除后无法被访问到。这里和插入时一样,有一个很类似的细节:

front++后可能会越界,统一的处理方式如下:

front = (front + 1)% MIXSIZE; 

代码:

void CycleQueuePop(CycleQueue* pcq)
{
	assert(pcq);
	pcq->front = (pcq->front + 1) % MIXSIZE;
}

返回队列长度

求循环队列长度的公式:

len = (rear - front + MIXSIZE) % MIXSIZE;

代码:

int CycleQueueLen(CycleQueue* pcq)
{
	assert(pcq);
	return (pcq->rear - pcq->front + MIXSIZE) % MIXSIZE;
}

返回队列头元素

队列头元素的下标为front。

代码:

CQueueDataType CycleQueueFront(CycleQueue* pcq)
{
	assert(pcq);
	return pcq->a[pcq->front];
}

判满

循环队列满的标志:

(rear + 1)% MIXSIZE == front; 

代码:

bool CycleQueueFull(CycleQueue* pcq)
{
	assert(pcq);
	return (pcq->rear + 1) % MIXSIZE == pcq->front;
}

 完! 

————————————————————————————————————————————————————————————

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

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

相关文章

蓝桥杯基础练习详细解析一(代码实现、解题思路、Python)

试题 基础练习 数列排序 资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200 输入格式 第…

同城桶装水订水送水小程序:为水站打开新局面;

你是否还在为传统的电话、微信订水方式而烦恼&#xff1f;效率低下、管理混乱&#xff0c;是时候拥抱变革&#xff0c;让生意更上一层楼了&#xff01;同城送水小程序&#xff0c;一键解决你的所有痛点&#xff0c;让您的水站焕发新生。 水站系统功能亮点&#xff1a; 1. 一键…

无货源电商上货成功解析,如何选用采集和上货软件提高工作效力

最近大家是不是有一个疑惑&#xff1f;就是咱们在上货的过程中会出现解析失败 不论怎么上传&#xff0c;数据都是失败的&#xff0c;上传成功率很低很低&#xff0c;换了各种的上货软件 几百的几千的软件都用过了效果还是不行&#xff1f;和代理宣传的不符&#xff1f;什么日传…

如何成功将自己开发的APP上架到应用商店

如何成功将自己开发的APP上架到应用商店 随着移动应用市场的蓬勃发展&#xff0c;开发一款优秀的APP已成为许多企业和个人的首要选择。然而&#xff0c;成功上架并有效推广APP至关重要。本文将逐步介绍完整的上架流程&#xff0c;包括准备所需材料、注册开发者账户、进行APP备…

【Python实战】——神经网络识别手写数字

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

学习Python的第二天

下载工具 PyCharm Community Edition 2023.3.4 下载环境 Python3.10.4 目录 1.初识python 1.1 Python的起源 1.2 为什么学习Python 2.什么是编程语言 2.1 语言的概念 2.2 为什么不使用中文来与计算机交流呢 3.python环境安装 4.第一个python程序 5.python解释器 5…

人才推荐 | 纺织科学与工程博士,经验丰富的面料专家和采购专家

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

一款炫酷的python形状绘制动画库

这个库让复杂数学概念的可视化变得既简单又有趣&#xff0c;无论是线性代数、微积分&#xff0c;还是更高级的数学主题&#xff0c;Manim都能让它们栩栩如生&#xff0c;特别适合于制作数学视频和演示文稿。 特点 动画生成&#xff1a; Manim库提供了一套丰富的工具和方法&…

C++ unordered_set和unordered_map

哈希 1. unordered_set/unordered_map1.1 背景1.2 unordered_set1.2.1 特性1.2.2 常用方法 1.3 unordered_map1.3.1 特性1.3.2 常用方法 2. 哈希2.1概念2.2 哈希冲突2.2.1哈希函数2.2.2 解决哈希冲突2.2.2.1 闭散列2.2.2.2 开散列 1. unordered_set/unordered_map 1.1 背景 之…

新台阶——蓝桥杯单片机省赛第十四届程序设计题目

在做十四届题目之前&#xff0c;常常听学长说&#xff0c;十四届以前拿省一真的是右手就行&#xff0c;并不相信&#xff0c;在经历十四届痛苦的大量修bug和优化之后&#xff0c;或许学长的话真说对了几分。话不多说&#xff0c;我们开始一起完成单片机第十四届程序设计题目。 …

【】(综合练习)博客系统

在之前的学些中&#xff0c;我们掌握了Spring框架和MyBatis的基本使用&#xff0c;接下来 我们就要结合之前我们所学的知识&#xff0c;做出一个项目出来 1.前期准备 当我们接触到一个项目时&#xff0c;我们需要对其作出准备&#xff0c;那么正规的准备是怎么样的呢 1.了解需求…

基于傅里叶描述子的手势动作识别,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

ubuntu22.04物理机双系统手动分区

ubuntu22.04物理机双系统手动分区 文章目录 ubuntu22.04物理机双系统手动分区1. EFI系统分区2. 交换分区3. /根分区4. /home分区分区后的信息 手动分区顺序&#xff1a;EFI系统分区(/boot/efi)、交换分区(/swap)、/根分区、/home分区。 具体参数设置&#xff1a; 1. EFI系统分…

02. 【Android教程】开发环境搭建

在学习 Android 应用开发之前&#xff0c;我们先要完成环境的搭建&#xff0c;它将帮助我们将 Java 代码编译打包生成最终的 Android 安装包。本教程在 Mac 下完成安装&#xff0c;Windows 和 Linux 步骤类似&#xff0c;不同之处会着重区分。 1. 文件清单 Java SE Developmen…

JVM的知识

什么是JVM 1.JVM&#xff1a; JVM其实就是运行在 操作系统之上的一个特殊的软件。 2.JVM的内部结构&#xff1a; &#xff08;1&#xff09;因为栈会将执行的程序弹出栈。 &#xff08;2&#xff09;垃圾99%的都是在堆和方法区中产生的。 类加载器&#xff1a;加载class文件。…

芯片中小公司ERP系统的业务流程:揭秘数字化管理的新篇章

随着信息技术的飞速发展&#xff0c;ERP(企业资源规划)系统已成为众多企业实现数字化管理的重要工具。对于芯片中小公司而言&#xff0c;ERP系统更是提升运营效率、优化资源配置的关键所在。那么&#xff0c;芯片中小公司的ERP系统究竟是如何运作的呢?让我们一同揭开其业务流程…

Spatialite坐标投影并计算面积

将坐标转为WGS_1984_UTM_Zone_48N&#xff08;32648&#xff09;后再计算其面积&#xff1a; -- 转换坐标系并计算面积&#xff08;平方米&#xff09; SELECT ST_Area(ST_Transform(GeomFromText(POLYGON((106.763 26.653, 106.763 26.626, 106.815 26.625, 106.809 26.666, …

我们使用 Postgres 构建多租户 SaaS 服务时踩的坑

原文 Our Multi-tenancy Journey with Postgres Schemas and Apartment。这篇和之前发出的「如何使用 Postgres 对一个多租户应用分片」相呼应。 多租户 (Multip-tenancy) 是当下的热门话题。我对多租户应用程序的定义是一个能够服务于多个客户的软件系统&#xff0c;每个客户都…

有名的爬虫框架 colly 的特性及2个详细采集案例

一. Colly概述 前言&#xff1a;colly 是 Go 实现的比较有名的一款爬虫框架&#xff0c;而且 Go 在高并发和分布式场景的优势也正是爬虫技术所需要的。它的主要特点是轻量、快速&#xff0c;设计非常优雅&#xff0c;并且分布式的支持也非常简单&#xff0c;易于扩展。 框架简…

javaSSM游泳馆日常管理系统IDEA开发mysql数据库web结构计算机java编程maven项目

一、源码特点 IDEA开发SSM游泳馆日常管理系统是一套完善的完整企业内部系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;MAVEN方式加载&#xff0c;系统具有完整的源代码和…