数据结构 - 队列 [动画+代码注释超详解],萌新轻松上手!!!

一. 队列的概念

队列是一种特殊的线性表,用于存储元素,并且按照先进先出(First In First Out)的顺序进行管理,这意味着最先加入队列的元素将会是最先从队列中被移除的元素

队列的原型:只允许在一端进行插入数据的操作,在另一端进行删除数据的操作

队列的原则:队列中的元素遵循先进先出的原则 

队列的两个经典操作:

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

出队列:队列的删除操作叫做出队列,进行操作的一端称为队头

二. 队列的结构

现实中的队列

 当我们去银行取款机排队取钱的过程就是队列,我们从队尾进入,依次取钱,取完钱之后从队头离开

三. 队列的实现

队列的实现有两种方式

一. 用数组实现

优点

  • 快速访问:数组允许随机访问,可以快速访问任何一个元素,特别是在入队和出队操作时,可以直接通过索引来访问队头和队尾。
  • 内存连续:数组是连续内存的数据结构,这可能有助于提高缓存效率,因为连续的内存块更有可能一起被加载到CPU缓存中。

缺点

  • 固定大小:数组的大小在初始化时固定,这意味着队列的容量有一个上限。如果队列满了,就需要执行昂贵的数组扩展操作,通常涉及分配一个更大的数组并复制现有元素。
  • 空间浪费:在使用数组实现循环队列时,即使数组中还有空间,队列也可能报告已满,这是因为循环使用的逻辑问题导致的空间利用不充分。

二. 用链表实现

优点

  1. 动态大小:链表提供了动态大小的能力,队列可以根据需要增长和缩小,不存在固定的容量限制。
  2. 内存利用率高:链表只在需要时分配内存,且只为实际存储的元素分配,这减少了内存浪费。

缺点

  1. 内存分配开销:链表的每个新元素都可能需要内存分配(除非使用内存池技术),这可能比连续的内存分配(如数组)更昂贵。
  2. 访问速度慢:链表不支持随机访问,访问任何位置的元素都需要从头开始遍历,这使得某些操作比数组慢。
  3. 额外内存需求:每个链表节点需要额外的内存空间来存储指向下一个节点的指针,这增加了每个元素的内存开销。

总结:不过整体上使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。下面我将用链表的结构来实现队列

1. 初始化队列

1.1 链式结构表示队列

在使用链表实现队列的时候,定义一个节点结构QNode,为队列中的每个元素提供一个容器,使得元素能连接起来

typedef int QDataType;typedef struct QListNode  //定义一个节点
{struct QListNode* pNext; //指针域QDataType data; //数据域
}QNode;

1.2 队列的结构

普通链表通常只需要一个头指针来访问链表的起始位置,而队列为了支持高效的入队和出队操作,需要同时维护队头和队尾两个指针,我们通常会定义一个额外的结构体,这个结构体包括了指向队头的指针和指向队尾的指针。

typedef struct Queue
{QNode* front; // 队头指针QNode* rear;  // 队尾指针
} Queue;         // 队列结构体别名

1.3 队列的初始化 

接下来就是创建一个初始化函数,对队列里的元素进行初始化

void QueueInit(Queue* q)
{assert(q);  // 断言队列指针q不为NULL,确保不对NULL指针进行操作,提高程序的安全性。q->front = NULL; // 将队列的前端指针设置为NULL,表示队列为空,即队列中没有元素。q->rear = NULL;  // 将队列的后端指针也设置为NULL,与队列为空的状态一致,因为没有元素可以指向。
}

2. 销毁队列

从对头开始,进行释放空间,最后让队头队尾指针置为NULL

void QueueDestory(Queue* q)
{assert(q);            // 断言队列指针不为空QNode* cur = q->front;  // 创建一个变量,从队头开始销毁队列节点while (cur){QNode* next = cur->next;  // 保存当前节点的下一个节点free(cur);                 // 释放当前节点的内存cur = next;                // 移动到下一个节点}q->front = NULL;  // 将队列的头指针置为空,表示队列已被销毁q->rear = NULL;   // 将队列的尾指针置为空,表示队列已被销毁
}

3. 入队列

申请一个新的节点链接到尾部,然后让尾指针,指向新节点  需要注意的是:若队列中无数据,我们需要让队头和队尾都指向这个新的节点

void QueuePush(Queue* q, QDataType x)
{assert(q);  // 断言队列指针不为空QNode* newnode = (QNode*)malloc(sizeof(QNode));  // 分配新节点的内存空间if (newnode == NULL){printf("malloc fail\n");  // 如果内存分配失败,打印错误信息exit(-1);                  // 退出程序}newnode->data = x;   // 将数据存储到新节点中newnode->next = NULL;  // 新节点的下一个节点指针为空if (q->front == NULL)  // 如果队列为空{q->front = newnode;  // 将新节点设置为队列的头节点q->rear = newnode;   // 将新节点设置为队列的尾节点}else{q->rear->next = newnode;  // 将新节点链接到队列尾部q->rear = newnode;        // 更新队列的尾节点为新节点}
}

4. 出队列

释放队头的节点,并将队头更新到下一个元素。需要注意的是,如果队列中只有一个数据,在释放了队头的节点之后,要让队尾和队头的指针置空

void QueuePop(Queue* q)
{assert(q);  // 断言以确保队列指针 'q' 不是 NULL,保证这是一个有效的指针。assert(!QueueEmpty(q));  // 断言以确保队列不为空,仅当队列非空时才能进行出队操作。// 如果队列中只有一个节点,即队首和队尾是同一个节点if (q->front->next == NULL){q->front = NULL;  // 将队首指针置为空q->rear = NULL;   // 将队尾指针置为空,因为队列要变为空队列}else{// 如果队列中不止一个节点,则将队首节点出队QNode* head = q->front->next;  // 临时保存新的队首节点free(q->front);  // 释放当前的队首节点的内存q->front = head;  // 更新队首指针为新的队首节点}
}

5. 获取队列的队头元素

返回队头指针指向的数据即可

QDataType QueueFront(Queue* q)
{assert(q);assert(!QueueEmpty(q));//检测队列是否为空return q->front->data;//返回队头指针指向的数据
}

6.获取队列的队尾元素

返回队尾指针指向的数据即可

QDataType QueueBack(Queue* q)
{assert(q);assert(!QueueEmpty(q));//检测队列是否为空return q->rear->data;//返回队尾指针指向的数据
}

7. 检测队列是否为空

判断队头的指针是否指向空

bool QueueEmpty(Queue* q)
{assert(q);return q->front == NULL;
}

8. 获取队列中有效元素个数

队列中有效元素个数,即队列中的结点个数。我们只需遍历队列,统计队列中的节点数并返回即可

int QueueSize(Queue* q)
{assert(q);  // 断言以确保队列指针 'q' 不是 NULL,保证这是一个有效的指针。QNode* cur = q->front;  // 创建一个指针 'cur',用来遍历队列,从队首开始int count = 0;  // 初始化计数器 'count',用于统计队列中的元素数量// 遍历队列,直到 'cur' 指针为空,即到达队列末尾while (cur){count++;  // 对每个节点进行计数cur = cur->next;  // 将 'cur' 指针移动到下一个节点}return count;  // 返回队列中的元素总数
}

"Yesterday is history, tomorrow is a mystery, but today is a gift. That is why it is called the present."

昨日已成历史,明天充满未知,而今天是一份礼物,这就是为什么它被称为‘现在’。

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

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

相关文章

HTTP、模块化

HTTP协议 包括请求行、请求头、请求体 http常见请求方法: url统一资源请求符,其本身也是一个字符串 响应体的内容格式是非常灵活的,常见的响应体格式有: 1.HTML 2.CSS 3. JavaScript 4.图片 5.视频 6.JSON 响应状态码: IP本身是一个数字…

20240427纳米多孔石墨烯力学性能的调控

文献来源:Tuning the mechanical properties of nanoporous graphene: a molecular dynamics study DOI:https://orcid.org/0000-0003-3598-5439 结论:在这项工作中,使用经典MD模拟分析了扶手椅和锯齿形的NPG(纳米多…

Ubuntu2004 CMake 使用基础

一、环境安装 win10安装wsl ubuntu2004 #windows c盘工程目录建立软链 ln -s /mnt/c /home/vrviu/ 安装cmake、c编译工具 apt install -y cmake g 二、CMakeLists.txt讲解 准备工作 首先,在/home/vrviu 目录建立一个 cmake 目录 以后我们所有的 cmake 练习都会放…

如何借模板助力小程序开发

不论是奶茶店还是其他行业,想要开发小程序,乔拓云都为你提供了便捷的方案。无需复杂的编程技术,通过套用模板的方式,即可快速打造专属小程序。 在线访问乔拓云官方网站,免费注册账号后,即可进入商城小程序的…

RabbitMQ知识点总结(一)

为什么要使用RabbitMQ? 异步,解耦,削峰。 异步 提高效率;一个挂了,另外的服务不受影响。 解耦 增加或减少服务比较方便。 削峰 每天0点到16点,A系统风平浪静,每秒并发数量就100个。结果每次到了16点到…

SecretFlow学习指南(2)学习路径

目录 一、模块架构 二、模块详解 三、算法协议 四、学习路线 一、模块架构 良好的分层设计可以提高开发效率和可维护性,满足不同用户的需求。隐语从上到下一共分为六层。 ●产品层:通过白屏化产品提供隐语整体隐私计算能力的输出,让用户简…

ZYNQ--PL读写PS端DDR数据

PL 和PS的高效交互是zynq 7000 soc开发的重中之重,我们常常需要将PL端的大量数 据实时送到PS端处理,或者将PS端处理结果实时送到PL端处理,常规我们会想到使用DMA 的方式来进行,但是各种协议非常麻烦,灵活性也比较差,本节课程讲解如何直接通过AXI总 线来读写PS端ddr的数据…

【Pytorch】(十三)模型部署: TorchScript

文章目录 (十三)模型部署: TorchScriptPytorch动态图的优缺点TorchScriptPytorch模型转换为TorchScripttorch.jit.tracetorch.jit.scripttrace和script的区别总结trace 和script 混合使用保存和加载模型 (十三)模型部署: TorchScr…

掌静脉识别关键技术研究综述

掌静脉识别作为一种新兴的红外生物识别技术,因其高安全性、活体检测性等优势已成为当前生物特征识别领域中的研究热点之一。近年来,该领域的大量研究通过引入深度学习方法推动了掌静脉识别技术的发展。为了掌握掌静脉识别领域最新研究现状及发展方向&…

ddos云服务器有哪些防御方法和优势

本文将介绍云服务器遇到DDoS攻击的应对方法,包括流量清洗、负载均衡、防火墙设置和CDN加速等。同时,文章还介绍了ddos云服务器的防御优势,包括高防护能力、自动化防御、实时监控和报警以及弹性扩展等。通过这些防御方法和ddos云服务器的应用&…

React复习笔记

基础语法 创建项目 借助脚手架,新建一个React项目(可以使用vite或者cra,这里使用cra) npx create-react-app 项目名 create-react-app是React脚手架的名称 启动项目 npm start 或者 yarn start src是源文件index.js相当于Vue的main.js文件。整个…

vue3 ——笔记 (条件渲染,列表渲染,事件处理)

条件渲染 v-if v-if 指令用于条件性地渲染一块内容,只有v-if的表达式返回值为真才会渲染 v-else v-else 为 v-if 添加一个 else 区块 v-else 必须在v-if或v-else-if后 v-else-if v-else-if 是v-if 的区块 可以连续多次重复使用 v-show 按条件显示元素 v-sh…

【Linux系统化学习】生产者消费者模型(阻塞队列和环形队列)

目录 生产者消费者模型 什么是生产者消费者模型 为什么要使用生产者消费者模型 生产者消费者模型的优点 为什么生产者和生产者要互斥? 为什么消费者和消费者要互斥? 为什么生产者和消费者既是互斥又是同步? 基于BlockingQueue的生产者…

26版SPSS操作教程(高级教程第十六章)

目录 前言 粉丝及官方意见说明 第十六章一些学习笔记 第十六章一些操作方法 多维尺度分析 不考虑个体差异的多维尺度分析模型 假设数据 具体操作 结果解释 选择不同距离的排列方式 考虑个体差异的多维尺度分析模型(INDSCAL,individual differ…

[C++ QT项目实战]----系统实现双击表格某一行,表格数据不再更新,可以查看该行所有信息,选中表更新之后,数据可以继续更新

前言 在需要庞大的数据量的系统中,基于合适的功能对数据进行观察和使用至关重要,本篇在自己项目实战的基础上,基于C QT编程语言,对其中一个数据功能进行分析和代码实现,希望可以有所帮助。一些特殊原因,图片…

车道分割YOLOV8-SEG

车道分割YOLOV8-SEG,训练得到PT模型,然后转换成ONNX,OPENCV的DNN调用,支持C,PYTHON,ANDROID开发 车道分割YOLOV8-SEG

数据污染对大型语言模型的潜在影响

大型语言模型(LLMs)中存在的数据污染是一个重要问题,可能会影响它们在各种任务中的表现。这指的是LLMs的训练数据中包含了来自下游任务的测试数据。解决数据污染问题至关重要,因为它可能导致结果偏倚,并影响LLMs在其他…

python三维交互可视化工具plotly使用

三维数据可视化工具使用 import plotly.graph_objects as go import numpy as np# 生成随机点 data np.random.uniform(-3,3,(100000, 2)) Z np.exp(-((data[:, 0] - 0)**2 / (2*1**2) (data[:, 1] - 0)**2 / (2*1**2)))scatter1 go.Scatter3d(xdata[:, 0], ydata[:, 1], …

【项目】仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器(Http板块)

【项目】仿muduo库One Thread One Loop式主从Reactor模型实现高并发服务器(Http板块) 一、思路图二、Util板块1、Splite板块(分词)(1)代码(2)测试及测试结果i、第一种测试ii、第二种…

关于discuz论坛网址优化的一些记录(伪静态)

最近网站刚上线,针对SEO做了些操作,为了方便网站网页被收录,特此记录下 1.开启伪静态 按照操作勾选所有项,然后点击查看伪静态规则 2.打开宝塔,找到左侧列表的网站,然后找到相应站点的设置。把discuz自动…