队列的实现与讲解

news2025/2/4 12:41:11

一.概念与结构

1.概念

只允许在⼀端进行插⼊数据操作,在另⼀端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)

入队列:进⾏插⼊操作的⼀端称为队尾

​ 出队列:进⾏删除操作的⼀端称为队头

注意:与上文讲到的栈对比,栈是先进后出,队列是先进先出。

2.结构

队列也可以数组和链表的结构实现,使⽤链表的结构实现更优⼀些,因为如果使⽤数组的结构,出队列在数组头上出数据,效率会⽐较低。

数组头删时间复杂度:O(N) ** 数组尾插时间复杂度:O(1)

单链表头删时间复杂度:O(1) 单链表尾插时间复杂度:O(N)

 因此下午队列的实现中,我们采用链表的结构来进行。

二.队列的实现

queue.h

完整头文件如下。

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode//队列节点的结构,即单链表节点的结构
{
	QDataType data;
	struct QueueNode* next;
}QNode;
typedef struct Queue//队列的结构,定义指向队列头尾的指针,以及队列节点的个数
{
	QNode* phead;
	QNode* ptail;
	QDataType size;
}Q;

void QueueInit(Q*);

//入队列,队尾
void QueuePush(Q*, QDataType);

//出队列,队头
void QueuePop(Q*);

//队列判空
bool QueueEmpty(Q*);

//取队头数据
QDataType QueueFront(Q*);

//取队尾数据
QDataType QueueBack(Q*);

//队列有效元素个数
int QueueSize(Q*);

void QueueDestroy(Q*);

分析:

1.此处我们定义了两个结构体,一个是队列的基本结构QNode,一个是用来表示队列的队头,队尾和元素个数的Q。

2.Q存在的意义主要是为了便于后续表示队列的队头,队尾时可以简化二级指针的个数,且更加清晰。

test.c

相关测试代码如下。

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
void QueueTest01()
{
	Q q;//定义队列
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	///
	printf("head:%d\n", QueueFront(&q));
	printf("tail:%d\n", QueueBack(&q));
	printf("size:%d\n", QueueSize(&q));
	QueuePop(&q);
	QueueDestroy(&q);
}

int main()
{
	QueueTest01();
	return 0;
}

队列的初始化

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
void QueueInit(Q* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

分析:

1.需注意,我们此处传入的是指针Q*而非QNode*。

2.其余置空断言操作如常。

队列的销毁

void QueueDestroy(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* pcur = pq->phead;
	while (pcur)
	{
		QNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

分析:

1.由于每个节点与之前的链表类似,都是动态开辟的,因此我们通过Q*找到队列头phead,之后逐个释放节点即可。

2.依次释放之后置空并将元素个数置为0即可。

节点的创建

首先创建一个专门用来生产节点的函数,避免后续插入时代码的冗余。

QNode* BuyNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

注意:在创建节点后记得把size++。

队尾插入数据——入队列

void QueuePush(Q* pq, QDataType x)
{
	assert(pq);
	if (pq->phead == NULL)
		pq->phead = pq->ptail = BuyNode(x);
	else
	{
		pq->ptail->next = BuyNode(x);
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}

分析:与链表的尾插原理基本相同。

队列判空

在进入删除之前,首先需要判断队列数据个数是否为空。

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

此处通过使用size个数是否为0进行判空也可。

队头删除数据——出队列

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

	//只有一个节点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

分析:

1.首先判断队列数据是否为空。

2.之后考虑两种情况,如果队列只有一个节点,那么直接删除之后需要将队头和队尾都置为空。

3,如果队列不止存在一个节点,同链表的删除原理类似。

返回队头数据

QDataType QueueFront(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}

返回队尾数据

QDataType QueueBack(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}

输出队列数据个数

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

	//不规范且时间复杂度O(n)
	//int size = 0;
	//QNode* pcur = pq->phead;
	//while (pcur)
	//{
	//	size++;
	//	pcur = pcur->next;
	//}
	//return size;


	return pq->size;

}

分析:

1.如果我们不在最初引入变量size记录数据个数,由于队列是链表结构无法直接通过下标访问元素个数,需要逐个遍历记录,时间复杂度为O(n)。

2.直接返回size的话就可以减少时间消耗。

三.完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
void QueueInit(Q* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}


QNode* BuyNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}


void QueuePush(Q* pq, QDataType x)
{
	assert(pq);
	if (pq->phead == NULL)
		pq->phead = pq->ptail = BuyNode(x);
	else
	{
		pq->ptail->next = BuyNode(x);
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}



bool QueueEmpty(Q* pq)
{
	assert(pq);
	return pq->phead == NULL && pq->ptail == NULL;
}
void QueuePop(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	//只有一个节点的情况,避免ptail变成野指针
	if (pq->ptail == pq->phead)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}



QDataType QueueFront(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->phead->data;
}


QDataType QueueBack(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->ptail->data;
}


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

	//不规范且时间复杂度O(n)
	//int size = 0;
	//QNode* pcur = pq->phead;
	//while (pcur)
	//{
	//	size++;
	//	pcur = pcur->next;
	//}
	//return size;


	return pq->size;

}



void QueueDestroy(Q* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* pcur = pq->phead;
	while (pcur)
	{
		QNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

以上就是关于队列的概念,结构和用链表方式实现队列的讲解,欢迎各位大佬前来支持斧正!!!

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

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

相关文章

美联储巨亏背后的秘密

听说美联储报告称亏损已破2000亿美元&#xff0c;这一数字无疑触动了市场的敏感神经。 亏损的直接原因是美联储在加息周期期间&#xff0c;为了维持短期利率在目标水平&#xff0c;向金融机构支付的利息超过了其持有债券的利息收入。 然而&#xff0c;美联储官员强调&#xff…

学习C语言(23)

整理今天的学习内容 1.文件的概念 使用文件是为了将数据永久化地保存 按照文件功能&#xff0c;在程序设计中一般把文件分成两类&#xff1a; 每个文件都有一个唯一的文字标识&#xff0c;文字标识常被称为文件名&#xff0c;文件名包含文件路径&#xff0c;文件名主干和文件…

如何快速切换电脑的ip地址

在当今的数字化时代&#xff0c;IP地址作为网络身份的重要标识&#xff0c;其重要性日益凸显。无论是出于保护个人隐私的需要&#xff0c;还是为了访问特定的网络服务等&#xff0c;快速切换电脑的IP地址已成为许多用户的迫切需求。本文将为你介绍几种实用的方法&#xff0c;帮…

【Hadoop】改一下core-site.xml和hdfs-site.xml配置就可以访问Web UI

core-site.xml&#xff1a; hdfs-site.xml&#xff1a; 所有的都改为0.0.0.0 就可以访问Web UI 原因&#xff1a; 使用 0.0.0.0 作为绑定地址时&#xff0c;实际会将服务监听在所有可用的网络接口上。这意味着&#xff0c;任何从外部访问的请求都可以通过任何网络适配器连接到…

黑神话:仙童,数据库自动反射魔法棒

黑神话&#xff1a;仙童&#xff0c;数据库自动反射魔法棒 Golang 通用代码生成器仙童发布了最新版本电音仙女尝鲜版十一及其介绍视频&#xff0c;视频请见&#xff1a;https://www.bilibili.com/video/BV1ET4wecEBk/ 此视频介绍了使用最新版的仙童代码生成器&#xff0c;将 …

算法笔记(六)——链表

文章目录 两数相加两两交换链表中的节点重排链表合并 K 个升序链表K个一组翻转链表 技巧: 画图观察指针指向&#xff1b;添加虚拟头节点&#xff1b;可以多定义几个节点&#xff1b;快慢双指针&#xff1b; 常见操作&#xff1a; 定义new head逆序时&#xff0c;头插 ListNode*…

带你深入浅出设计模式:八、适配器模式:代码世界中的万能转换器

此为设计模式第八谈&#xff01; 用总-分-总的结构和生活化的例子给你讲解设计模式&#xff01; 码农不易&#xff0c;各位学者学到东西请点赞收藏支持支持&#xff01; 开始部分&#xff1a; 总&#xff1a;适配器模式主要解决的问题是已有类的接口与所需的接口不匹配的问题…

[Python学习日记-38] Python 中的函数的名称空间

[Python学习日记-38] Python 中的函数的名称空间 简介 名称空间 作用域查找顺序 简介 在前面学习函数的时候我们发现&#xff0c;函数内部也有一个内存空间是用于存储函数自己的一些变量的&#xff0c;及时这个变量名与外部的变量名一样是也没关系&#xff0c;Python 会优先…

CGLib动态代理和JDK动态代理Demo、ASM技术尝鲜

本文主要介绍CGLib和JDK动态代理的使用&#xff0c;不对源码进行深入分析。代码可直接复制使用。 类型 机制 回调方式 适用场景 效率 JDK动态代理 委托机制。代理类和目标类都实现了同样的接口。InvocationHandler持有目标类。代理类委托InvocationHandler去调用目标类原…

令牌主动失效机制范例(利用redis)注释分析

介绍该机制 令牌生成 在需要限流的场景中&#xff0c;系统会根据一定的速率生成令牌&#xff0c;存储在 Redis 中。可以设定每秒生成的令牌数量。 令牌获取 当用户请求时&#xff0c;系统会从 Redis 中获取令牌。可以使用原子性操作&#xff08;如 DECR&#xff09;来确保令牌…

SHAP分析

SHAP分析&#xff08;SHapley Additive exPlanations&#xff09;是一种基于博弈论的解释机器学习模型输出的方法。它提供了一种统一的方式来解释模型的预测结果&#xff0c;量化每个特征对模型预测的贡献&#xff0c;能够为复杂的机器学习模型&#xff08;如随机森林、梯度提升…

C语言基础(8)之操作符(2)(详解)

目录 1. 操作符汇总表 2. 关系操作符 3. 条件操作符 4. 逗号表达式 5. 下标引用、函数调用和结构成员 5.1 下标引用 5.2 函数调用操作符 5.3 结构成员 6. 操作符的属性 6.1 操作符的优先级 大家好呀&#xff01;上篇文章中我们详细讲解了操作符的前半部分&#xff0c…

【成长day】SuperPointSuperGlue(01): Superpoint论文算法学习与对应源码解析

两年前自己在实习公司做过superpoint相关的工作&#xff0c;当时是负责利用superpoint代替slam前端的特征点部分&#xff0c;来达到把特征点相关的处理放到推理计算平台上减轻CPU压力并且精度无损的目的&#xff0c;最终也是成功完成了这部分工作。但是当时没有留下任何的记录&…

YOLOv8 基于MGD的知识蒸馏

YOLOv8 基于MGD的知识蒸馏 接着上一篇我们介绍了YOLOv8的剪枝方案和代码&#xff0c;本篇文章将剪枝后的模型作为学生模型&#xff0c;剪枝前的模型作为教师模型对剪枝模型进行蒸馏&#xff0c;从而进一步提到轻量模型的性能。 Channel-wise Distillation (CWD) 问题和方法 …

IDM下载器如何下载网盘文件 IDM下载器支持哪些网盘

不用开通会员&#xff0c;也能高速下载网盘文件。使用IDM下载加速器&#xff0c;直接从服务器高速下载文件&#xff0c;轻松突破网盘限速。掌握IDM下载网盘文件的技巧&#xff0c;不仅可以节省会员费用&#xff0c;还可以大幅提高下载效率。有关IDM下载器如何下载网盘文件&…

【Linux:线程控制】

目录 线程的创建与等待&#xff1a; ​编辑 代码中tid是什么&#xff1f; 如何看待线程函数传参&#xff1f; ​编辑 ​编辑创建多线程&#xff1a;​编辑 终止多线程&#xff1a; 线程分离&#xff1a; 线程的创建与等待&#xff1a; void *threadrun(void *args) {int …

QT 中如何保存matlab 能打开的.mat数据矩阵!

Windows 上安装并使用 MATIO 库来保存 MATLAB 格式的 .mat 文件&#xff0c;需要进行以下步骤&#xff1a; 1. 下载并安装 CMake MATIO 使用 CMake 构建项目&#xff0c;因此你需要先安装 CMake。 前往 CMake 官网下载适用于 Windows 的安装程序并安装。 2. 下载 MATIO 库源…

Unity基础-矩阵-坐标转换结果的个人理解+数学公式说明

想做一些渲染效果做到头大&#xff0c;根本很多空白&#xff0c;完全无法实现&#xff0c;只能先暂停一下&#xff0c;重新学习矩阵 目录 Unity基础-数学矩阵 1.我们利用最简单的“转换矩阵”&#xff0c; 2.然后&#xff0c;视图坐标又是如何 3.最后就是剪裁坐标 3.1 - 其…

硬件设计-利用环路设计优化PLL的输出性能

目录 前言 问题描述 问题分析步骤 杂散源头排查 245.76M 参考相噪&#xff1a; 30.72M VCXO的相噪性能测试如下: 解决方案 前言 LMK04832是TI 新发布的低抖动双环去抖模拟时钟&#xff0c; 其最高输出频率可以到达3250MHz&#xff0c; 输出抖动极低&#xff0c;3200MHz…

MySQL 中的数据库锁和表锁

在 MySQL 数据库中&#xff0c;为了保证数据的一致性和完整性&#xff0c;会使用各种类型的锁。其中&#xff0c;数据库锁和表锁是比较常见的两种锁类型。 一、数据库锁和表锁的概念 &#xff08;一&#xff09;数据库锁 数据库锁是对整个数据库进行锁定&#xff0c;限制对数…