【数据结构】循环队列(数组实现)

news2024/11/13 9:25:03

目录

一、循环队列定义

怎么使一个数组在逻辑上呈“环状”呢?

 二、循环队列与顺序队列的差异

1、存储方式:

2、操作方式:

3、空间利用率:

4、循环队列判断队空的方式:

5、循环队列判断队满的方式

完整测试代码及注释: 

总结:


一、循环队列定义

将顺序存储队列的元素的一维数组首尾相接,形成一个环状,如下图所示,这种形式表示的队列称为循环队列。循环队列仍然是顺序队列结构,只是逻辑上和前面的顺序队列有所不同。

#define MAXLEN 6           // 定义环形队列的最大长度为 6

typedef int DataType;     // 定义数据类型为整型

typedef struct CircularQueue  // 定义环形队列的结构体
{
    DataType a[MAXLEN];   // 定义存储数据的数组
    int front, rear;      // 定义队头和队尾指针
    int size;             // 定义队列元素个数
} CQueue;

void InitCQueue(CQueue* q)  // 初始化环形队列
{
    q->front = q->rear = 0; // 队头和队尾指针都指向队列的开始位置
    q->size = 0;            // 队列元素个数为 0,即初始为空队列
}

怎么使一个数组在逻辑上呈“环状”呢?

在数据结构中,可以使用一个 front 指针和一个 rear 指针来表示环状队列的队头和队尾位置,当 rear 指针移动到数组的最后一个位置时,如果再有元素需要入队,那么应该将 rear 指针指向数组的第一个位置。同样地,当 front 指针移动到数组的最后一个位置时,如果还有元素需要出队,那么应该将 front 指针指向数组的第一个位置。

具体实现方法如下:

  1. 初始化:定义一个数组和两个指针 front 和 rear,初始化时,将 front 指针和 rear 指针都指向数组的第一个位置。

  2. 入队:如果队列未满,则将元素插入 rear 指向的位置,然后将 rear 指针后移一位。当 rear 指针移动到数组的最后一个位置时,若队列未满,则将 rear 指针指向数组的第一个位置。

  3. 出队:如果队列非空,则将队头元素取出,然后将 front 指针后移一位。当 front 指针移动到数组的最后一个位置时,只要队列非空,就将 front 指针指向数组的第一个位置。

假设队列开辟的数组单元数为MAXSIZE,它的数组下标在0~MAXSIZE-1之间,若使队头或队尾增1,且使front和rear指针对应的数组下标保持在数组范围内,可以利用取模运算实现。


例如,在下图所示的循环队列示意图最大空间为MAXSIZE=8,数组下标为0~7之间。

非空队时如图(2)中队头指针front指向队列中队头元素的前一个位置队尾指针rear 指向队列的队尾元素位置。

  •         入队时的队尾指针加1操作修改为: rear=(rear+1)%MAXSIZE;
  •         出队时的队头指针加1操作修改为:front=(front+1)%MAXSIZE;

入队代码实现: 

void CQueuePush(CQueue* q, DataType x)   // 元素入队
{
    assert(q);  // 判断 q 是否为空
    if (!CQueueFull(q))  // 如果队列未满
    {
        q->rear = (q->rear + 1) % MAXLEN;   // 队尾指针后移一位
        q->a[q->rear] = x;  // 在队尾处添加元素
        q->size++;  // 队列元素个数加 1
    }
    else    // 队列已满,无法添加数据
    {
        printf("队列已满,无法添加数据!\n");
        exit(-1);
    }
}

 出队代码实现: 

void CQueuePop(CQueue* q)   // 元素出队
{
    assert(q);  // 判断 q 是否为空
    if (!CQueueEmpty(q))    // 如果队列非空
    {
        q->front = (q->front + 1) % MAXLEN; // 队头指针后移一位
        q->size--;  // 队列元素个数减 1
    }
    else    // 队列已空,无法删除数据
    {
        printf("队列已空,无法删除数据!\n");
        exit(-1);
    }
}


 二、循环队列与顺序队列的差异

  • 1、存储方式:

    • 顺序队列:使用数组作为底层数据结构,按照顺序存储元素。
    • 循环队列:仍然使用数组作为底层数据结构,但是通过循环利用数组的空间,实现循环存储。
  • 2、操作方式:

    • 顺序队列:使用两个指针front和rear分别表示队头和队尾,元素入队时rear指针后移,元素出队时front指针后移。
    • 循环队列:同样使用两个指针front和rear表示队头和队尾,但是在数据满或空的状态下,指针继续向后移动的时候保持循环关系。
      • 入队时队尾指针+1:rear=(rear+1)%MAXSIZE;
      • 出队时队头指针+1:front=(front+1)%MAXSIZE;
  • 3、空间利用率:

    • 顺序队列:存储元素的空间是连续的,当队列未满但是数组的末尾已经被利用时,无法继续插入元素。
    • 循环队列:通过循环利用数组空间,解决了顺序队列存储空间的浪费问题,可以实现更高的空间利用率。

4、循环队列判断队空的方式:

图(1)中队头与队尾指针指向同一位置时为空队,判断方法与顺序队列一致。

 代码实现:

int CQueueEmpty(CQueue* q)  // 判断队列是否为空
{
    assert(q);  // 判断 q 是否为空
    if (q->front == q->rear)  // 通过队头和队尾指针是否相等,判断队列是否为空
    {
        return 1;   // 队列为空
    }
    return 0;       // 队列非空
}

5、循环队列判断队满的方式

由 图(3) 可见,循环队列解决了顺序队列中“假溢出”的现象,充分利用了固定长度的队列中的空间。我们知道,在长度不可增长的顺序队列中,判断队列是否队满的条件是rear==MAXLEN。那么在循环队列中,我们判断队满的方式则为:(rear+1)%MAXLEN==front;  


 

代码实现:

int CQueueFull(CQueue* q)   // 判断队列是否为满
{
    assert(q);  // 判断 q 是否为空
    if ((q->rear + 1) % MAXLEN == q->front)  // 通过队尾和队头指针是否相邻,判断队列是否为满
    {
        return 1;   // 队列为满
    }
    return 0;       // 队列未满
}

我们理解完顺序队列与循环队列的差异后,在固定长度代码的基础上对front、rear指针的移动判满操作进行修改即可得到循环队列的代码。


完整测试代码及注释: 

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>


#define MAXLEN 6           // 定义环形队列的最大长度为 6

typedef int DataType;     // 定义数据类型为整型

typedef struct CircularQueue  // 定义环形队列的结构体
{
    DataType a[MAXLEN];   // 定义存储数据的数组
    int front, rear;      // 定义队头和队尾指针
    int size;             // 定义队列元素个数
} CQueue;

void InitCQueue(CQueue* q)  // 初始化环形队列
{
    q->front = q->rear = 0; // 队头和队尾指针都指向队列的开始位置
    q->size = 0;            // 队列元素个数为 0,即初始为空队列
}

int CQueueFull(CQueue* q)   // 判断队列是否为满
{
    assert(q);  // 判断 q 是否为空
    if ((q->rear + 1) % MAXLEN == q->front)  // 通过队尾和队头指针是否相邻,判断队列是否为满
    {
        return 1;   // 队列为满
    }
    return 0;       // 队列未满
}

int CQueueEmpty(CQueue* q)  // 判断队列是否为空
{
    assert(q);  // 判断 q 是否为空
    if (q->front == q->rear)  // 通过队头和队尾指针是否相等,判断队列是否为空
    {
        return 1;   // 队列为空
    }
    return 0;       // 队列非空
}

void CQueuePush(CQueue* q, DataType x)   // 元素入队
{
    assert(q);  // 判断 q 是否为空
    if (!CQueueFull(q))  // 如果队列未满
    {
        q->rear = (q->rear + 1) % MAXLEN;   // 队尾指针后移一位
        q->a[q->rear] = x;  // 在队尾处添加元素
        q->size++;  // 队列元素个数加 1
    }
    else    // 队列已满,无法添加数据
    {
        printf("队列已满,无法添加数据!\n");
        exit(-1);
    }
}

void CQueuePop(CQueue* q)   // 元素出队
{
    assert(q);  // 判断 q 是否为空
    if (!CQueueEmpty(q))    // 如果队列非空
    {
        q->front = (q->front + 1) % MAXLEN; // 队头指针后移一位
        q->size--;  // 队列元素个数减 1
    }
    else    // 队列已空,无法删除数据
    {
        printf("队列已空,无法删除数据!\n");
        exit(-1);
    }
}

int CQueueTop(CQueue* q)   // 获取队首元素
{
    if (!CQueueEmpty(q))    // 如果队列非空
    {
        return q->a[q->front + 1];  // 返回队头下一个位置的元素
    }
    else    // 队列已空,无法获取队首数据
    {
        printf("队列已空,无法获取队首数据!\n");
        exit(-1);
    }
}

int CQueueTail(CQueue* q)   // 获取队尾元素
{
    if (!CQueueEmpty(q))    // 如果队列非空
    {
        return q->a[q->rear];   // 返回队尾位置的元素
    }
    else    // 队列已空,无法获取队尾数据
    {
        printf("队列已空,无法获取队尾数据!\n");
        exit(-1);
    }
}


int main()
{
	CQueue q;
	InitCQueue(&q);
	CQueuePush(&q, 1);
	CQueuePush(&q, 2);
	CQueuePush(&q, 3);
	CQueuePush(&q, 4);
	CQueuePush(&q, 5);
	CQueuePop(&q);
	CQueuePop(&q);
	CQueuePop(&q);
	CQueuePop(&q);
	//CQueuePop(&q);
	int x;
	x = CQueueTop(&q);
	printf("%d\n", x);
	x = CQueueTail(&q);
	printf("%d\n", x);
    return 0;
}

总结:

循环队列通过环形数组的设计,充分利用了存储空间,并实现了高效的元素入队和出队操作。在使用循环队列时,需要特别注意队列为空和队列满的判断,避免出现错误。

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

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

相关文章

simulink代码生成(九)—— 串口显示数据(纸飞机联合调试)

纸飞机里面的协议是固定的&#xff0c;必须按照协议配置&#xff1b; &#xff08;1&#xff09;使用EasyHEX协议&#xff0c;测试int16数据类型 测试串口发出的数据是否符合&#xff1f; 串口接收数据为&#xff1a; 打开纸飞机绘图侧&#xff1a; &#xff08;1&#xff09…

常见网络协议

1.DNS协议 &#xff08;域名系统&#xff09; DNS协议使用的端口号是53 位于OSI模型中的应用层 DNS系统的作用&#xff1a;将域名&#xff08;网址&#xff09;解析为IP地址。 DNS的基本原理是&#xff1a;将域名映射到IP地址 DNS工作流程 当用户给定一个域名&#xff0…

实验笔记之——服务器链接

最近需要做NeRF相关的开发,需要用到GPU,本博文记录本人配置服务器远程链接的过程,本博文仅供本人学习记录用~ 连上服务器 首先先确保环境是HKU的网络环境(HKU AnyConnect也可)。伙伴已经帮忙创建好用户(第一次登录会提示重新设置密码)。用cmd ssh链接ssh -p 60001 <u…

综合跨平台全端ui自动化测试框架Airtest——AirtestIDE录制微信小程序脚本教学

前言 有在自动化测试领域的小伙伴应该都知道&#xff0c;app和小程序自动化这一类的自动化测试在实际操作中有时候很棘手让人心烦&#xff0c;动不动就是用appium写代码脚本维护什么的&#xff0c;不仅步骤繁琐&#xff0c;环境配置方面也是繁琐无比&#xff0c;动不动就与客户…

【操作系统xv6】学习记录5--实验1 Lab: Xv6 and Unix utilities

ref:https://pdos.csail.mit.edu/6.828/2020/xv6.html 实验&#xff1a;Lab: Xv6 and Unix utilities 环境搭建 实验环境搭建&#xff1a;https://blog.csdn.net/qq_45512097/article/details/126741793 搭建了1天&#xff0c;大家自求多福吧&#xff0c;哎。~搞环境真是折磨…

前端push.js桌面通知库

push.js 官网&#xff1a;https://pushjs.org/ 安装 1,npm 安装方式 npm install push.js --save 2,script引入方式 <script src"https://cdnjs.cloudflare.com/ajax/libs/push.js/0.0.11/push.min.js"></script> 使用 1&#xff0c;获取用户许可…

UnityRenderStreaming使用记录(二)

记录一下发现的问题 1、网页经常出现一直转但是不出现播放按钮的问题 检查发现有几个js文件从外网加载&#xff0c;速度太慢导致的&#xff0c;下载到本地&#xff0c;重新打包webserver.exe就可以了 比如Receiver Sampled的网页在UnityRenderStreaming\WebApp\client\public…

elect函数可以设置等待时间,

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;贝叶斯滤波与Kalman估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能&#xff0c…

Mybatis-Plus乐观锁配置使用流程【OptimisticLockerInnerInterceptor】

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家:人工智能学习网站 1.乐观锁实现 1.配置插件 1.XML方式 <bean class"com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerI…

el-table表格动态添加列。多组数据拼接和多层级数据的处理

提示&#xff1a;el-table表格动态添加列 文章目录 前言一、多组数据拼接二、多层级处理三、实际应用中&#xff0c;为避免闪屏&#xff0c;可以表格数据统一渲染总结 前言 需求&#xff1a;富文本编辑器 一、多组数据拼接 <template><div class"test">…

【langchain】入门初探实战笔记(Chain, Retrieve, Memory, Agent)

1. 简介 1.1 大语言模型技术栈 大语言模型技术栈由四个主要部分组成&#xff1a; 数据预处理流程&#xff08;data preprocessing pipeline&#xff09;嵌入端点&#xff08;embeddings endpoint &#xff09;向量存储&#xff08;vector store&#xff09;LLM 终端&#xff…

mac环境下安装部署mysql5.7

下载安装包 进入官网下载MySQL5.7的安装包 https://www.mysql.com/downloads/ 安装包下载完成后双击pkg文件进行安装&#xff0c;无脑点下一步即可&#xff0c;注意安装完成后记得保存最后弹出框的密码 进入系统偏好设置&#xff0c;找到mysql&#xff0c;开启mysql服务…

人工智能教程(四):概率论入门

目录 前言 TensorFlow 入门 SymPy 入门 概率论入门 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站 在本系列的 上一篇文章 中&#xff0c;我们进一步讨论了矩阵和线性代数&#…

macbook录屏快捷键大全,教你快速录制视频

“有人知道macbook电脑有录屏快捷键吗&#xff0c;现在录屏的速度太慢了&#xff0c;每次打开都要浪费不少时间&#xff0c;要是有录屏快捷键&#xff0c;应该会快很多&#xff0c;有哪位大佬知道吗&#xff1f;教教我&#xff01;” 无论是在工作还是生活中&#xff0c;电脑已…

安卓Android Studio读写FM1208CPU卡源码

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-21818769070.11.6c46789elLwMzv&id615391857885 <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout x…

redis复习笔记02(小滴课堂)

分布式缓存Redis6常见核心配置讲解 查看配置文件&#xff1a; 创建配置文件&#xff1a; 配置完我们去验证一下&#xff1a; 启动成功就没有问题了。 可以看到redis日志。 然后我们就可以连接我们的redis了&#xff1a; 设置了密码就需要密码登录了。 如果登录了错误的密码也无…

C/C++汇编学习(二)——学习使用IDA pro

学习使用IDA Pro是一项很有价值的技能&#xff0c;特别是对于那些对逆向工程和软件安全分析感兴趣的人。以下是一些基本步骤和概念&#xff0c;帮助你熟悉IDA Pro的界面和操作。 1. 熟悉IDA Pro界面和基本操作 主界面布局 IDA Pro的主界面包含多个组件&#xff0c;每个组件都…

静态网页设计——个人简介网站

前言 使用经典前端三件套HTMLCSSJavascript编写了一个关于个人简介的静态网页&#xff0c;可以根据自己的需要&#xff0c;十分简单的进行修改。 首页 首页由上方的菜单栏以及菜单栏下面的轮播图组成&#xff0c;再往下走&#xff0c;是关于自己的兴趣爱好的部分&#xff0c…

UG/NX许可证使用效率提升新技术

UG/NX许可证使用效率提升新技术 UG&#xff08;Unigraphics NX&#xff09;是Siemens PLM Software公司出品的一个产品工程解决方案&#xff0c;它为用户的产品设计及加工过程提供了数字化造型和验证手段。近年来随着国家对知识产品保护的不断加强&#xff0c;以前使用盗版软件…

vue3+Ts+Hook的方式实现商城核心功能sku选择器

前言 Hooks是React等函数式编程框架中非常受欢迎的工具&#xff0c;随着VUE3 Composition API 函数式编程风格的推出&#xff0c;现在也受到越来越多VUE3开发者的青睐&#xff0c;它让开发者的代码具有更高的复用度且更加清晰、易于维护。 本文将通过CRMEB商城商品详情sku选择…