【算法与数据结构】队列的实现详解
文章目录
- 📝队列的概念及结构
- 🌠 队列的顺序实现
- 🌉初始化
- 🌠入队
- 🌉出队
- 🌠获取队列首元素
- 🌉获取队列尾部元素
- 🌠获取队列中有效元素个数
- 🌉 队列是否为空
- 🌠查看队列是否为满
- 🌉销毁队列
- 🌠测试
- 🌉 顺序队列的假溢出
- 🌠循环队列概念
- 🌉循环队列的初始化
- 🌠循环队列的入队操作
- 🌉 循环队列的出队操作
- 🌉 查看队首元素
- 🌠查看队尾元素
- 🌉查看队列是否为空
- 🌠查看队列是否已满
- 🌉 测试下循环队列
- 🚩总结
📝队列的概念及结构
- 队列的概念:
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 新添加的元素添加到队尾,只能从队头取出元素。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
- 队列特征如下:
入队(Enqueue):通过尾指针添加元素到队列尾部,即向队列中插入元素。
出队(Dequeue):通过头指针删除队列头部元素,即从队列中移除元素。
空队列:当头指针和尾指针相同时,表示队列为空。
满队列:当尾指针指向队列容量最大位置时,表示队列已满。
- 常用的队列结构包括数组和链表实现:
数组实现队列:使用一维数组存储元素,头指针和尾指针分别指向数组的下标位置。
链表实现队列:每个元素使用一个节点存储,头节点和尾节点通过指针链接实现队列。
🌠 队列的顺序实现
头文件:Queue_order.h
# define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>#define MAX_SIZE 100typedef int QDataType;typedef struct Queue
{QDataType data[MAX_SIZE];int front;int rear;int size;//记录队列中元素数量
}Queue;//初始化队列
void InitQueue(Queue* pq);
//入队操作
void EnQueue(Queue* pq,int value);
//出队
QDataType DeQueue(Queue* pq);
//获取队首元素
QDataType FrontQueue(Queue* pq);
//获取队尾元素
QDataType RearQueue(Queue* pq);//获取队列中有效元素个数
QDataType SizeQueue(Queue* pq);
//队列是否为空
QDataType IsEmpty(Queue* pq);
//队列是否为满
QDataType IsFull(Queue* pq);
//销毁队列
void DestroyQueue(Queue* pq);
🌉初始化
//初始化队列
void InitQueue(Queue* pq)
{pq->front = -1;pq->rear = -1;pq->size = 0;
}
front和rear都指向-1,表示队列中没有数据。size为0,表示队列中没有元素。
🌠入队
//入队
void EnQueue(Queue* pq, int value)
{if (pq->front == MAX_SIZE - 1){printf("队列满了,入队操作失败");//检查队列是否已满,如果front指针指向的下一个位置就是MAX_SIZE-1,表示队列已满,打印错误信息并返回。return;}if (pq->front == -1){//如果front为-1,表示队列为空,将front指针置0。pq->front = 0;}pq->rear++;//如果front为-1pq->data[pq->rear] = value;//表示队列为空pq->size++;//加完了别忘了size++
}
🌉出队
//出队
QDataType DeQueue(Queue* pq)
{if (-1 == IsEmpty){printf("队列为空!");return -1;//返回一个特定的值表示队列为空}int value = pq->data[pq->front];pq->front++;pq->size--;return value;
}
检查满是为了防止入队越界,front
为-1
时,表示队列为空,需要将front
置0,rear
后移一位指向新的元素位置,将元素值写入data
数组,size
计数增加。
🌠获取队列首元素
//获取队首元素
QDataType FrontQueue(Queue* pq)
{if (-1 == IsEmpty){printf("队列为空!");return -1;//返回一个特定的值表示队列为空}return pq->data[pq->front];
}
🌉获取队列尾部元素
//获取队列尾部元素
QDataType RearQueue(Queue* pq)
{if (0 == IsEmpty){printf("队列为空!");return -1;//返回一个特定的值表示队列为空}return pq->data[pq->rear];
}
🌠获取队列中有效元素个数
//获取队列中有效元素个数
QDataType SizeQueue(Queue* pq)
{return pq->size;
}
🌉 队列是否为空
//队列是否为空
QDataType IsEmpty(Queue* pq)
{if (pq->front == -1 || (pq->front > pq->rear)){//队列为空return -1;}else{//队列不为空return 1;}}
注意:在队列中,如果 front
指针大于 rear
指针,表示队列为空。这是因为 front 指针指向队列的第一个元素,而 rear
指针指向队列的最后一个元素。如果 front
指针大于 rear
指针,意味着队列中没有元素,或者已经出队了所有的元素。
考虑一个队列中有一个元素的情况。初始时 front
和 rear 都指向该元素,而当该元素出队时,front 指针会被移动到下一个位置,这时 front 指针就比 rear 指针大,表示队列已经为空。
🌠查看队列是否为满
//查看队列是否为满
QDataType IsFull(Queue* pq)
{return (pq->rear == MAX_SIZE - 1);
}
🌉销毁队列
//销毁队列
void DestroyQueue(Queue* pq)
{InitQueue(pq);//重新初始化队列,清空元素并释放内存
}
🌠测试
源文件Test.c
# define _CRT_SECURE_NO_WARNINGS 1
#include"Queue_order.h"int main()
{Queue Pq;InitQueue(&Pq);EnQueue(&Pq, 10);EnQueue(&Pq, 20);EnQueue(&Pq, 30);printf("Front element: %d\n", FrontQueue(&Pq));printf("Rear element: %d\n", RearQueue(&Pq));printf("Front element: %d\n", SizeQueue(&Pq));printf("Front element: %d\n", FrontQueue(&Pq));printf("Dequeued element: %d\n", DeQueue(&Pq));printf("Dequeued element: %d\n", DeQueue(&Pq));printf("Dequeued element: %d\n", DeQueue(&Pq));printf("Dequeued element: %d\n", DeQueue(&Pq)); // 尝试从空队列中出队printf("Queue size: %d\n", SizeQueue(&Pq));printf("Is queue empty? %s\n", IsEmpty(&Pq) ? "Yes" : "No");
}
🌉 顺序队列的假溢出
顺序队列的假溢出指的是,队列在结构上没有真正满溢,但是在逻辑上已经无法再插入新元素了。
在队尾指针已经指向数组的最后一个位置,但数组中仍然有空闲空间时,确实是队列溢出的情况,而不是假溢出。这种情况通常是由于没有充分利用队列所分配的存储空间所导致的,因此会造成队列操作的限制。
“假溢出” 通常用于表示队列中还有空闲空间,但因某种原因无法继续插入元素的情况,这可能是由于某些限制条件或错误的队列操作所导致的。例如,可能存在对队列的错误管理,使得无法正确地判断队列是否已满,导致了插入元素失败的情况。
举个例子:
在一个顺序队列中,队列大小为5,已包含四个元素 value1-2-3-4,rear在队尾4的位置。
当出队两个元素value1-2时,队列前面多出来了两位置
当你想再插入来两个数据时,队列确只能插入一个数据,但是队列其实不能插入数据了。
这是队列在结构上没有真正满溢,但是在逻辑上已经无法再插入新元素了。
怎么优化这个假溢出呢?两种常见的方法:
- 循环队列: 循环队列是一种特殊的顺序队列,通过将队列的数组视为一个循环的环形结构,使得在队列尾部插入元素时可以利用数组头部的空闲空间,从而解决了假溢出的问题。循环队列中,当队尾指针指向数组的末尾时,再插入元素时将其指向数组的起始位置,这样就形成了一个循环。通过这种方式,可以充分利用数组空间,避免了假溢出。
- 动态扩容: 动态扩容是在顺序队列满时,自动增加数组的大小以容纳更多元素。当队列满时,分配一个更大的数组,并将原有的元素复制到新数组中,然后释放原来的数组。这样可以确保队列始终有足够的空间来插入新的元素,从而避免假溢出。动态扩容的关键是选择适当的扩容策略,以及控制扩容时机,以避免频繁的扩容操作带来的性能开销。
🌠循环队列概念
循环队列是一种基于数组实现的队列数据结构,其特点是通过循环利用数组空间来实现队列的操作。循环队列的数组通常被看作一个环形的结构,队列的头部和尾部指针在数组中循环移动,使得当尾部指针到达数组末尾时,可以将其“循环”到数组的起始位置,从而避免了溢出或假溢出的情况。
循环队列看似循环,其实是固定数组不断往复的过程,我们可以模拟普通数组来实现:
如图:data 表示一个数据域,int 为类型,当然你也可以修改为任意自定义的类型,也可以是复杂的结构体类型。
MAX_SIZE :表示循环最大容量,可进行放数据的操作空间
rear代表尾指针,入队时移动。
front代表头指针,出队时移动。
size记录当前有效数据的多少
代码:
#define MAX_SIZE 5typedef struct {int data[MAX_SIZE];int front; // 队列头指针int rear; // 队列尾指针int size; // 队列当前元素个数
} Cir_Queue;
🌉循环队列的初始化
对于循环队列来说,front从0开始是合理的,因为数据数组是环状结构,front从0开始可以实现队列元素的循环利用。
rear从0开始表示队列此时为空,front和rear指针都指向数组第一个位置。
将队列当前元素个数size清零,表示队列为空。
// 初始化队列
void initQueue(Cir_Queue* queue)
{queue->front = 0;将队列头指针front设置为0。queue->rear = 0;将队列尾指针rear也设置为0。queue->size = 0;
}
🌠循环队列的入队操作
入队操作与顺序队列相同,只需将rear向后移动。但要注意,如果rear达到队列的上限,需从头开始移动。建议使用余数法,确保操作在队列空间内进行,避免一次错误导致整体崩溃。
要进行入队操作,得先判断队列是不是满了rear==front来判断队空,也可以用size。
// 入队操作
void enqueue(Cir_Queue* queue, int value)
{if (queue->size == MAX_SIZE) {printf("Queue is full. Enqueue operation failed.\n");return;}queue->data[queue->rear] = value;queue->rear = (queue->rear + 1) % MAX_SIZE; // 使用(rear+1)%MAX_SIZE更新rear指针,循环移动queue->size++;
}
检查队列是否已满,可通过判断size是否等于MAX_SIZE来确定。如果已满,则打印提示信息并返回;将数值value赋给data数组的rear索引位置,并使用(rear+1)%MAX_SIZE更新rear指针。这里使用模运算来实现rear指针的循环移动。当rear指向最后一个位置时,利用模运算使其指向第一个位置,实现循环利用数组。然后将size增加1,表示元素个数加1。
🌉 循环队列的出队操作
// 出队操作
int dequeue(Cir_Queue* queue){if (queue->size == 0){printf("Queue is empty. Dequeue operation failed.\n");return -1;}int value = queue->data[queue->front];queue->front = (queue->front + 1) % MAX_SIZE; // 循环移动queue->size--;return value;
}
使用(front+1)%MAX_SIZE
更新front
指针,这里也使用模运算实现front
指针的循环移动。当front
指向最后一个位置时,利用模运算让它指向第一个位置。
🌉 查看队首元素
// 查看队首元素
int front(Cir_Queue* queue)
{if (queue->size == 0) {printf("Queue is empty. Front operation failed.\n");return -1;}return queue->data[queue->front];
}
🌠查看队尾元素
// 查看队尾元素
int rear(Cir_Queue* queue)
{if (queue->size == 0) {printf("Queue is empty. Rear operation failed.\n");return -1;}return queue->data[(queue->rear - 1 + MAX_SIZE) % MAX_SIZE];
}
🌉查看队列是否为空
// 查看队列是否为空
int isEmpty(Cir_Queue* queue)
{return (queue->size == 0);
}
🌠查看队列是否已满
// 查看队列是否已满
int isFull(Cir_Queue* queue)
{return (queue->size == MAX_SIZE);
}
🌉 测试下循环队列
int main() {Cir_Queue queue;initQueue(&queue);enqueue(&queue, 10);enqueue(&queue, 20);enqueue(&queue, 30);enqueue(&queue, 40);printf("Front element: %d\n", front(&queue));printf("Dequeued element: %d\n", dequeue(&queue));printf("Dequeued element: %d\n", dequeue(&queue));printf("Dequeued element: %d\n", dequeue(&queue));enqueue(&queue, 80);enqueue(&queue, 90);printf("Front element: %d\n", front(&queue));printf("rear element: %d\n", rear(&queue));return 0;
}
🚩总结
感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘
相关文章:

【算法与数据结构】队列的实现详解
文章目录 📝队列的概念及结构🌠 队列的顺序实现🌉初始化🌠入队🌉出队🌠获取队列首元素🌉获取队列尾部元素🌠获取队列中有效元素个数🌉 队列是否为空🌠查看队列…...
GPT-3后的下一步:大型语言模型的未来方向
摘要: 本文将概述GPT-3后的下一步:大型语言模型的未来方向,包括技术发展趋势、应用场景、挑战与机遇。 引言: GPT-3是OpenAI于2020年发布的一款大型语言模型,它在自然语言处理领域取得了突破性进展。GPT-3的出现标志…...

基于机器学习的曲面拟合方法
随着科技的不断发展,机器学习成为了最近最热门的技术之一,也被广泛应用于各个领域。其中,基于机器学习的曲面拟合方法也备受研究者们的关注。曲面拟合是三维模型处理中的重要技术,其目的是用一组数据点拟合出平滑的曲面࿰…...

【C++从练气到飞升】03---构造函数和析构函数
🎈个人主页:库库的里昂 ✨收录专栏:C从练气到飞升 🎉鸟欲高飞先振翅,人求上进先读书。 目录 ⛳️推荐 一、类的6个默认成员函数 二、构造函数 1. 构造函数的概念 2. 构造函数的定义 3. 构造函数的特性 三、析构函…...
mybatis转义字符
编写SQL中会用到<,>,<,> 等,但是在mybatis中不可以这么写,与xml文件的元素<>冲突,所以需要转义。整理转义字符如下: 符号原始字符转义字符大于>>大于等于>>小于<<小于等于<<和&&a…...

vue3 实现一个tab切换组件
一. 效果图 二. 代码 文件 WqTab.vue: <template><div ref"wqTabs" class"wq-tab"><template v-for"tab in tabs" :key"tab"><div class"tab-item" :class"{ ac: tabActive tab.key }" c…...

JSONObject在Android Main方法中无法实例化问题
目录 前言一、Main(非安卓环境)方法下运行二、安卓坏境下运行三、why? 前言 原生的json,即org.json.JSONObject; 在Android Studio中的Main方法里运行报错,但在安卓程序运行过程正常 一、Main(非安卓环境)方法下运行 static void test() {try {// 创建一个 JSON …...

京津冀协同发展:北京·光子1号金融算力中心——智能科技新高地
京津冀协同发展是党中央在新的历史条件下提出的一项重大国家战略,对于全面推进“五位一体”总体布局,以中国式现代化全面推进强国建设、民族复兴伟业,具有重大现实意义和深远历史意义。随着京津冀协同发展战略的深入推进,区域一体…...
aspnetcore使用jwt时一直提示401 authorization
测试aspnetcore使用Jwt做认证授权的时候,一直提示401 Authorization 最后发现问题所在,希望能有所帮助 1.检查注册了认证和授权中间件 缺一不可 /*认证*/app.UseAuthentication();/*授权*/app.UseAuthorization();2.检查swagger的配置项 builder.Servic…...

三款文案自动生成器,帮你轻松生成原创文案
文案在今天已经成为了许多企业和个人推广产品和服务的重要手段。然而,对于很多人来说,写作文案并非易事。有时候,我们可能会遇到文案灵感枯竭的情况,或者花费大量时间在寻找合适的词句上。但是,别担心!现在…...
多线程并发模拟实现与分析:基于Scapy的TCP SYN洪水攻击实验研究
简介 实现基于Python实现的多线程TCP SYN洪水攻击。该实例利用Scapy库构造并发送TCP SYN数据包,通过多线程技术模拟并发的网络攻击行为。 实现原理 SYN Flood攻击是一种经典的分布式拒绝服务(DDoS)攻击方式,利用了TCP协议握手过…...

git命令行提交——github
1. 克隆仓库至本地 git clone 右键paste(github仓库地址) cd 仓库路径(进入到仓库内部准备提交文件等操作) 2. 查看main分支 git branch(列出本地仓库中的所有分支) 3. 创建新分支(可省…...

LM2903BIDR比较器芯片中文资料规格书PDF数据手册参数引脚图功能封装尺寸图
产品概述: M393B 和 LM2903B 器件是业界通用 LM393 和 LM2903 比较器系列的下一代版本。下一代 B 版本比较器具有更低的失调电压、更高的电源电压能力、更低的电源电流、更低的输入偏置电流和更低的传播延迟,并通过专用 ESD 钳位提高了 2kV ESD 性能和输…...
遍历list过程中调用remove方法
1、普通for循环遍历List删除指定元素,list.remove(index) List<String> nameList new ArrayList<>(Arrays.asList("张三", "李四", "王五", "赵六")); nameList.add("张七"); nameList.add("…...
Java解决罗马数字转整数
Java解决罗马数字转整数 01 题目 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 …...

无忧·企业文档v2.1.9新版本发布,全新升级,新变化让文档管理更无忧!
项目介绍 JVS是企业级数字化服务构建的基础脚手架,主要解决企业信息化项目交付难、实施效率低、开发成本高的问题,采用微服务配置化的方式,提供了 低代码数据分析物联网的核心能力产品,并构建了协同办公、企业常用的管理工具等&…...

【C语言_指针[2]_复习篇】
目录 一、数组名的理解 二、使用指针访问一维数组中的每个元素 三、一维数组传参的本质 四、冒泡排序 五、二级指针 六、指针数组 七、指针数组模拟二维数组 一、数组名的理解 1. 一般情况下,数组名就是数组首元素的地址。 2. 特殊情况1:sizeof(数…...
Rust 泛型使用过程中的 <T> 和 ::<T> 的区别
Rust 的泛型语法中,<T> 和 ::<T> 有不同的用途和上下文,但它们都与泛型有关。 <T> 在类型定义中 当你在定义函数、结构体、枚举或其他类型时,使用 <T> 来表示泛型参数。例如: fn identity<T>(x:…...

C语言 ——注释
1.1 单行注释 - 语法:// 待注释的内容 - 位置:可放在代码后,称之为行尾注释; 也可放代码上一行,称作行上注释。 c // 这是单行注释文字 1.2 多行注释 - 语法:/* 待注释的内容 */ - 注意:多⾏…...
C# 协程的使用
C# 中的协程是通过使用 yield 关键字来实现的,它们允许在方法的执行中暂停和继续。协程通常用于处理异步操作、迭代和状态机等情况。以下是关于C#协程的介绍、使用场景以及优缺点的概述: 介绍: 在 C# 中,协程是通过使用 yield 语…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...