【算法与数据结构】队列的实现详解
文章目录
- 📝队列的概念及结构
- 🌠 队列的顺序实现
- 🌉初始化
- 🌠入队
- 🌉出队
- 🌠获取队列首元素
- 🌉获取队列尾部元素
- 🌠获取队列中有效元素个数
- 🌉 队列是否为空
- 🌠查看队列是否为满
- 🌉销毁队列
- 🌠测试
- 🌉 顺序队列的假溢出
- 🌠循环队列概念
- 🌉循环队列的初始化
- 🌠循环队列的入队操作
- 🌉 循环队列的出队操作
- 🌉 查看队首元素
- 🌠查看队尾元素
- 🌉查看队列是否为空
- 🌠查看队列是否已满
- 🌉 测试下循环队列
- 🚩总结
📝队列的概念及结构
- 队列的概念:
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出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 语…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...