【数据结构与算法】详解循环队列:基于数组实现高效存储与访问

   

            💓 博客主页:倔强的石头的CSDN主页 

           📝Gitee主页:倔强的石头的gitee主页

            ⏩ 文章专栏:《数据结构与算法》

                                  期待您的关注

1b7335aca73b41609b7f05d1d366f476.gif

目录

一、引言

🍃队列的概念

🍃循环队列的概念

🍃为什么使用数组实现循环队列

二、循环队列的结构定义

三、循环队列的接口实现

🍃队列初始化

🍃入队列

🍃出队列

🍃取队首元素

🍃取队尾元素

🍃判空

🍃判满

🍃队列销毁

四、C语言实现代码

🍃Circular_Queue.h   //循环队列头文件

🍃Circular_Queue.c   //循环队列源文件

🍃test.c           //main函数测试文件

测试结果

五、循环队列的应用场景

六、总结


一、引言

🍃队列的概念

队列(Queue)是一种常见的数据结构,它遵循先进先出(FIFO)的原则,即最早进入队列的元素将最先被移除。队列在计算机科学中有广泛的应用,比如任务调度、网络流量控制、打印任务管理等。然而,当我们在处理固定大小的空间时,传统的队列实现可能会遇到空间浪费的问题。为了解决这个问题,我们引入了循环队列(Circular Queue)的概念。

关于队列的详细介绍,请参考前置文章

【数据结构与算法】使用单链表实现队列:原理、步骤与应用-CSDN博客

🍃循环队列的概念

循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。相比于传统的队列实现,循环队列能够更有效地利用存储空间,并在数组大小固定的情况下实现队列的无限循环。在本文中,我们将详细探讨如何使用数组来实现循环队列,并分析其优势和应用场景。

🍃为什么使用数组实现循环队列

循环队列的实现方式主要是基于数组的,但也可以采用其他数据结构,如链表。不过,在实际应用中,数组实现循环队列的方式更为常见和高效。

基于数组实现循环队列的特点和优势

  1. 空间利用率高:通过将数组的最后一个位置与第一个位置相连,循环队列能够充分利用数组的存储空间,避免传统队列在多次入队和出队操作后可能出现的空间浪费现象。
  2. 操作简便:在数组实现中,可以通过简单的数学运算(如取模运算)来更新头指针和尾指针,实现入队和出队操作。这使得循环队列的操作非常简便和高效。
  3. 时间复杂度低:无论是入队还是出队操作,循环队列的时间复杂度都是O(1),即常数时间复杂度。这意味着无论队列中有多少元素,入队和出队操作所需的时间都是固定的。

循环队列在逻辑上的结构是这样的 

但在物理上的结构是这样的

二、循环队列的结构定义

包含

  • 指向数组的指针,这是循环队列的底层结构
  • 指向队首和队尾的整型变量front和rear
  • 循环队列的空间大小k

typedef int CQueueDataType;
typedef struct MyCircularQueue//循环队列结构定义
{CQueueDataType* a;int front;int rear;int k;
} MyCircularQueue;

三、循环队列的接口实现

🍃队列初始化

  • 动态开辟一块循环队列结构体大小的空间
  • 为数组指针的指向地址分配一块动态申请的内存,大小为k+1个空间,但实际使用k个(不申请k个是为了区别队列空和队列满,保留一个空间)
  • front和rear初始为0(要注意rear初始为0,意味着指向的是队尾的下一个元素)
  • k初始化为输入的值
  • 最后返回该队列的地址
MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化
{MyCircularQueue* tmp = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));tmp->a = (CQueueDataType*)malloc(sizeof(CQueueDataType) * (k + 1));tmp->front = tmp->rear = 0;tmp->k = k;return tmp;
}

🍃入队列

  • 首先对形参接收的地址判空
  • 然后判断队列是否满
  • 如果有空间可用的话,在rear指向的位置插入数据
  • 调整rear的位置,向后移动注意考虑循环的问题(rear+1)%(k+1),先对rear+1再对数组长度取模

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //入队列
{assert(obj);if (myCircularQueueIsFull(obj))return false;obj->a[obj->rear] = value;obj->rear = (obj->rear + 1) % (obj->k + 1);return true;
}

🍃出队列

  • 首先对形参接收的地址判空
  • 然后判断队列是否为空
  • 如果有数据可出的话,直接调整front的位置即可(不过应当考虑循环值溢出的问题)(front+1)%(k+1)
  • 先对front+1再对数组长度取模

bool myCircularQueueDeQueue(MyCircularQueue* obj) //出队列
{assert(obj);if (myCircularQueueIsEmpty(obj))return false;obj->front = (obj->front + 1) % (obj->k + 1);return true;
}

🍃取队首元素

  • 首先对形参接收的地址判空
  • 然后判断队列是否为空(空队列无数据可取)
  • 然后返回front位置的元素即可

int myCircularQueueFront(MyCircularQueue* obj) //取队首元素
{assert(obj);if (myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}

🍃取队尾元素

  • 首先对形参接收的地址判空
  • 然后判断队列是否为空(空队列无数据可取)
  • 队尾元素是rear位置的前一个元素,考虑到直接-1可能会出错,正确的位置应该是(rear - 1 + k + 1) % (k + 1),也可以简化成(rear  +k ) % (k + 1)
  • 返回该位置数据即可

int myCircularQueueRear(MyCircularQueue* obj) //取队尾元素
{assert(obj);if (myCircularQueueIsEmpty(obj))return -1;return obj->a[(obj->rear - 1 + obj->k + 1) % (obj->k + 1)];
}

🍃判空

  • 对形参接收的地址判空
  • 然后返回front==rear的结果
bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判空
{assert(obj);return obj->front == obj->rear;
}

🍃判满

  • 对形参接收的地址判空
  • 队列满的条件理应是rear+1==front,但考虑到队列是一个"环形"的,要考虑值的溢出,所以改为(rear + 1 )% (k +1)==front
bool myCircularQueueIsFull(MyCircularQueue* obj) //判满
{assert(obj);return (obj->rear + 1) % (obj->k + 1)==(obj->front);
}

🍃队列销毁

  • 对形参接收的地址判空
  • 然后释放动态申请的数组的空间
  • front、rear、k都重置为0
  • 最后释放循环队列结构体的空间
void myCircularQueueFree(MyCircularQueue* obj) //循环队列销毁
{free(obj->a);obj->front = obj->rear = 0;obj->k = 0;free(obj);obj = NULL;
}

四、C语言实现代码

🍃Circular_Queue.h   //循环队列头文件

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int CQueueDataType;
typedef struct MyCircularQueue//循环队列结构定义
{CQueueDataType* a;int front;int rear;int k;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k); //循环队列初始化bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);//入队列bool myCircularQueueDeQueue(MyCircularQueue* obj);//出队列int myCircularQueueFront(MyCircularQueue* obj);//取队首元素int myCircularQueueRear(MyCircularQueue* obj); //取队尾元素bool myCircularQueueIsEmpty(MyCircularQueue* obj); //判空bool myCircularQueueIsFull(MyCircularQueue* obj);//判满void myCircularQueueFree(MyCircularQueue* obj); //循环队列销毁

🍃Circular_Queue.c   //循环队列源文件

#include"Circular_Queue.h"MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化
{MyCircularQueue* tmp = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));tmp->a = (CQueueDataType*)malloc(sizeof(CQueueDataType) * (k + 1));tmp->front = tmp->rear = 0;tmp->k = k;return tmp;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //入队列
{assert(obj);if (myCircularQueueIsFull(obj))return false;obj->a[obj->rear] = value;obj->rear = (obj->rear + 1) % (obj->k + 1);return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) //出队列
{assert(obj);if (myCircularQueueIsEmpty(obj))return false;obj->front = (obj->front + 1) % (obj->k + 1);return true;
}int myCircularQueueFront(MyCircularQueue* obj) //取队首元素
{assert(obj);if (myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) //取队尾元素
{assert(obj);if (myCircularQueueIsEmpty(obj))return -1;return obj->a[(obj->rear - 1 + obj->k + 1) % (obj->k + 1)];
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判空
{assert(obj);return obj->front == obj->rear;
}bool myCircularQueueIsFull(MyCircularQueue* obj) //判满
{assert(obj);return (obj->rear + 1) % (obj->k + 1)==(obj->front);
}void myCircularQueueFree(MyCircularQueue* obj) //循环队列销毁
{free(obj->a);obj->front = obj->rear = 0;obj->k = 0;free(obj);obj = NULL;
}

🍃test.c           //main函数测试文件

#include"Circular_Queue.h"void test1()
{int k = 0;scanf("%d", &k);MyCircularQueue* CQ = myCircularQueueCreate(k);//创建循环队列并初始化if (myCircularQueueIsEmpty(CQ))printf("队列空\n");myCircularQueueEnQueue(CQ, 1);//插入五个数据myCircularQueueEnQueue(CQ, 2);myCircularQueueEnQueue(CQ, 3);myCircularQueueEnQueue(CQ, 4);myCircularQueueEnQueue(CQ, 5);if (myCircularQueueIsEmpty(CQ))printf("队列空\n");elseprintf("队列非空\n");if (myCircularQueueIsFull(CQ))printf("队列满\n");elseprintf("队列非满\n");printf("队首元素:%d\n", myCircularQueueFront(CQ));printf("队尾元素:%d\n", myCircularQueueRear(CQ));while (!myCircularQueueIsEmpty(CQ))//依次打印队首元素并删除{printf("%d ", myCircularQueueFront(CQ));myCircularQueueDeQueue(CQ);}printf("\n");myCircularQueueFree(CQ);
}int main()
{test1();return 0;
}

测试结果

五、循环队列的应用场景

循环队列在实际应用中有着广泛的用途。以下是一些常见的应用场景:

  1. 任务调度:在操作系统中,任务调度器通常使用队列来管理待执行的任务。循环队列可以有效地处理这些任务,确保它们按照先进先出的顺序被执行。由于操作系统的资源有限,使用循环队列可以最大化地利用这些资源,避免不必要的空间浪费。

  2. 网络通信:在网络通信中,数据包经常需要在不同的节点之间传输。循环队列可以用于在节点上管理这些数据包,确保它们按照正确的顺序被发送和接收。特别是在路由器和交换机等网络设备中,循环队列可以有效地处理大量的数据包,提高网络性能。

  3. 打印机管理:在打印系统中,多个打印任务可能需要同时发送到打印机。循环队列可以用于管理这些打印任务,确保它们按照接收的顺序被打印出来。使用循环队列可以避免打印任务的混乱和丢失,提高打印效率。

  4. 模拟系统:在模拟系统中,如模拟银行排队系统或模拟医院挂号系统等,循环队列可以模拟现实中的排队情况。通过循环队列的入队和出队操作,可以模拟客户的到来和离开,以及服务员的接待过程。这有助于分析系统的性能和瓶颈,优化服务流程。

六、总结

循环队列是一种利用数组循环特性实现队列操作的数据结构。它通过维护头指针和尾指针来管理队列的入队和出队操作,实现了对固定大小空间的高效利用。

循环队列在任务调度、网络通信、打印机管理以及模拟系统等多个领域都有广泛的应用。

通过本文的介绍和分析,我们可以看到循环队列在解决实际问题时具有显著的优势和灵活性。因此,掌握循环队列的实现原理和应用方法对于提高编程能力和解决实际问题具有重要意义。

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

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

相关文章

DIY灯光特效:霓虹灯动画制作教程

下面我们根据这张霓虹灯案例,教大家如何用智能动物霓虹灯闪烁的效果,大家可以根据思路,实现自己想要的动效效果,一起动手来做吧。 即时设计-可实时协作的专业 UI 设计工具 设置背景 新建画板尺寸为:800PX^600PX,设置背景色#120527。 绘制主题 输入自己喜欢文案,轮廓化,具体…

网络基础-协议

一、ARP 通过IP得到Mac 首先会查看缓存的arp表中是否有相应的IP和Mac对应关系&#xff0c;如果有直接进行包封装。如果没有则进行广播当对应的地址就收到广播包后会根据arp中的源地址进行单播返回相应的IP和Mac对应关系。 arp -a 查看现有的arp缓存 二、RARP反向地址解析 通过…

好公司也需要在好的价格买入

在《战胜华尔街》的第七章&#xff0c;林奇先生还进一步支出在合适的价格买入的重要性。他指出“那些股价线上涨幅度超过了收益线的股票&#xff0c;一般要么是横盘整理&#xff0c;要么是股价回调到与其盈利水平比较相当的合理价位。”因此&#xff0c;“在大牛市中&#xff0…

酒店多商户版微信小程序源码

一站式预订新体验 一、引言&#xff1a;打破传统&#xff0c;开启智能预订新时代 在数字化浪潮的推动下&#xff0c;传统的酒店预订方式已经逐渐无法满足现代人的需求。为此&#xff0c;一款集众多酒店商户于一体的“酒店多商户版小程序”应运而生&#xff0c;以其便捷、智能…

江门电子行业实施MES系统前后对比

在江门电子行业实施MES系统之前和之后的对比可以涉及以下几个方面&#xff1a; 生产效率提升&#xff1a;实施MES系统后&#xff0c;江门电子行业可以实现生产过程的实时监控和优化&#xff0c;减少生产中的浪费和停机时间&#xff0c;提高生产效率。 质量控制改善&#xff1a;…

【机器学习】机器学习重要方法——深度学习:理论、算法与实践

文章目录 引言第一章 深度学习的基本概念1.1 什么是深度学习1.2 深度学习的历史发展1.3 深度学习的关键组成部分 第二章 深度学习的核心算法2.1 反向传播算法2.2 卷积神经网络&#xff08;CNN&#xff09;2.3 循环神经网络&#xff08;RNN&#xff09; 第三章 深度学习的应用实…

webworker 入门教程

参考&#xff08;阮一峰的网络日志&#xff09;&#xff1a;https://www.ruanyifeng.com/blog/2018/07/web-worker.html Web Worker 的作用&#xff1a; 就是为 JavaScript 创造多线程环境&#xff0c;允许主线程创建 Worker 子线程&#xff0c;将一些任务分配给后者运行。在主…

解释一下在React中,什么是“渲染Props”模式,以及它与使用Hooks之前的状态管理有何不同?

在React中&#xff0c;"渲染Props"模式是一种组件设计模式&#xff0c;它通过将一个函数作为prop传递给组件&#xff0c;允许父组件定义子组件的渲染逻辑。这种模式使得组件更加灵活和可复用&#xff0c;因为它们可以接受一个渲染函数来决定如何渲染自己。 渲染Prop…

简单分享Python语言(发现其实并不难)

一. Python基础 Python是一种解释型语言&#xff0c;这意味着开发者可以在代码被编写后立即执行它们&#xff0c;而无需编译。Python的基本语法简单明了&#xff0c;以下是一些基础知识点&#xff1a; 变量和数据类型&#xff1a;Python支持多种数据类型&#xff0c;包括整型&…

Python教程:深入理解Python中的__init__()方法

在Python中&#xff0c;__init__()方法是一个特殊的方法&#xff0c;它在创建类的实例时自动调用。这个方法通常用于初始化实例的状态。本文将详细介绍__init__()方法的工作原理、使用场景以及如何有效地使用它。 1. __init__()方法的基础 1.1 什么是__init__()方法&#xff1f…

视频监控解决方案:视频平台升级技术方案(下)

目录 1 项目概况 2 项目需求 2.1 视频感知资源扩充 2.2 视频支撑能力升级 2.3 视频应用能力升级 3 技术设计方案 3.1系统总体架构 3.2视频感知资源扩充设计 3.3 视频支撑能力升级设计 3.4 视频应用能力升级设计 3.4.1视频资源目录管理 3.4.2标签管理 3.4.3设备智能…

KEIL使用小工具

怎样把数组数据导出&#xff1a; KeiL 调试时保存watchwindow的参数变量到文件 KeiL 调试时保存watchwindow的参数变量到文件_keil持续记录变量值到文件-CSDN博客

MySQL----undo log回滚日志原理、流程以及与redo log比较

回滚日志 回滚日志&#xff0c;保存了事务发生之前的数据的一个版本&#xff0c;用于事务执行时的回滚操作&#xff0c;同时也是实现多版本并发控制&#xff08;MVCC&#xff09;下读操作的关键技术。 如何理解Undo Log 事务需要保证原子性&#xff0c;也就是事务中的操作要…

常用框架-Spring MVC

常用框架-Spring MVC 1、什么是Spring MVC?2、Spring MVC有哪些优点?3、Spring MVC的主要组件有哪些?4、DispatcherServlet是什么?5、Spring MVC框架的控制器是什么?6、Spring MVC的控制器是不是单例模式?7、控制器默认以单例模式创建会带来什么问题?怎么解决?8、描述下…

Nvidia Isaac Sim图编程OmniGraph 入门教程 2024(6)

Nvidia Isaac Sim 入门教程 2024 版权信息 Copyright 2023-2024 Herman YeAuromix. All rights reserved.This course and all of its associated content, including but not limited to text, images, videos, and any other materials, are protected by copyright law. …

知行合一与思行合一

“知行合一”和“思行合一”都是重要的哲学概念&#xff0c;强调了思维与实践的关系&#xff0c;但侧重点有所不同。以下是对这两个概念的详细解释、历史背景及具体实践示例。 知行合一 知行合一是由明代哲学家王阳明提出的一个重要思想&#xff0c;强调知识与行动的统一。 …

P7771 【模板】欧拉路径

网址如下&#xff1a; P7771 【模板】欧拉路径 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 忘掉了输出欧拉回路的方法&#xff0c;搞了我好久 关于欧拉回路的知识可以看我之前的博客&#xff1a; 一点关于欧拉回路的总结-CSDN博客 代码如下&#xff1a; #include<q…

学习笔记——动态路由——RIP(距离矢量协议)

一、距离矢量协议 1、距离矢量协议 矢量行为&#xff1a;协议收到一个路由之后&#xff0c;查看是否可以加入到本地的路由表中&#xff0c;如果可以加入&#xff0c;则可以传递&#xff0c;如果不可以加入&#xff0c;则无法传递。 距离矢量路由协议 RIP基于距离矢量算法(又…

记录外部EEPROM读写有问题排查过程

由于EEPROM的IIC驱动是用模拟IO写的 1.用示波器抓IIC的波形 2.将SDA和SCLK和GND引脚用线引出,探头连接 3.检查并确保探头衰减是X1而不是X10。 4.软件触发写EEPROM&#xff0c;示波器设置下降沿捕获 5.如果有波形&#xff0c;对波形进行Decode&#xff0c;看解码出来的数据是否…

使用 Reqable 在 MuMu 模拟器进行App抓包(https)

1、为什么要抓包&#xff1f; 用开发手机应用时&#xff0c;查看接口数据不能像在浏览器中可以直接通过network查看&#xff0c;只能借助抓包工具来抓包&#xff0c;还有一些线上应用我们也只能通过抓包来排查具体的问题。 2、抓包工具 实现抓包&#xff0c;需要一个抓包工具…