设计循环队列(c语言)

前言

在上一篇文章中我们了解了关于循环队列的基本特性:

d20ac53cf2374538ab6e197201cdc6d3.png

1、当rear == front时,表示队列为空

2、当rear + 1 = front时,表示队列已满

        当我们需要实现循环队列时,通常会选择使用链表或数组来存储队列中的元素。而使用数组来实现循环队列是一种更高效和方便的方法。

        事实上,数组是没有办法像上图所示的那样围成一个圈,它的实际情况应该是这样的:

back和front表示的是数组下标而非指针

75157fbc2567455e80e97e0ee79f27f9.png

        我们虽然可以通过back == front判断循环队列为空,但是当push 4后,由于是循环队列所以此时back会回到front的位置,这样就与back == front为循环队列空的判断条件冲突,所以该如何区分队列为空还是为满?

7035c3481132496bac7a52de00921c19.png

为此我们想到了多开辟一个空间的操作(该空间也可以用于存放数据),即可以存放的数据个数为k个则开辟空间个数为k+1个:

74b64b3f72474a1d8c95565c723e9970.png

多开辟的内个空间起到一个暂时中转的作用 

这样就解决back无处安放的窘境,此时由于空间已满,故开始出队,没出队一个元素front++:

73b560ec765f45379d842ae710bec17a.png

有了多余空间后再进行入队操作,此时back++后还要令back = back % (k+1):

59bc0d0f2ec94110998284a397fd20d8.png

上面提到的取模问题是该题目的最大难点,之所以要进行取模运算就是为了让back可以回到原来的位置处然后入队,我们拿具体的数字来让大家更好的理解:

假设此时队列中能且已经存放了四个有效数据,back此时的位置应该位于我们为其开辟的中转位置处值为4(因为我们要用back的值来表示每一次入队时的下标,所以每当入队一次后back都要++一次为了下一次的元素入队做准备)然后我们选择两次出队pop掉了1和2,接着我们push 5时,就应该向额外开辟的空间中push 5,此时back++后值为5,但是已知两次出队后在前面空出来两个空间(即使是一个也可以)且队列为循环队列,所以这时候就应该将back移到前面去,让back的值变为0(利用数组的循环特性),而我们只能利用取模运算%来达到这一目的(在字符逆序问题中我们也是用%来解决无效旋转问题)

关于数组的循环特性的解释:

        数组具有固定大小和连续内存空间的特点。当队列元素达到数组末尾时,如果还有剩余空间,我们希望能够利用这些空间继续存储新元素。通过取模运算可以将尾指针重新回到数组开头位置,使得队列成为一个闭合回路

——————————下面我们正式写一道关于如何设计循环队列的OJ题——————————

622. 设计循环队列 - 力扣(LeetCode)

1、初始化循环队列

  1. 声明一个包含数组、front下标、back下标、以及数组元素个数信息的结构体
  2. 为该结构体申请一个内存空间
  3. 为数组申请k+1个内存空间
  4. 初始化front下标和back下标为0、数组元素个数为k(该函数的形参为k)
  5. 返回申请的结构体地址
typedef struct {int* a;int front;  //front和back均表示下标int back;int k;      //这里k我们表示
} MyCircularQueue;//初始化循环队列
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->a = (int*)malloc(sizeof(int)*(k+1));obj->front = 0;obj->back = 0;obj->k = k;return obj;
}

2、判断循环队列是否为空

  1. 只需要利用判断front == back的结果是真是假即可(循环队列特性)
//判断循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->back;
}

3、判断循环队列是否已满

  1. 想象中判断已满应该是back+1 == front,它所传达的意思是当back表示下一个位置的下标的值等于front表示的下标的值相同时循环队列已满,但是我们在这里不能简单的这样写,由于我们利用数组的循环特性以及取模运算,所以要计算出back的下一个位置的下标的值就应该用(back+1后的值)% (实际的空间大小即k+1),然后将结果于front表示的下标的值比较
//判断循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->back+1) % (obj->k+1) == obj->front;
}

4、入队操作

  1. 先判断队列是否已满,若满则返回false
  2. 若未满则将要入队的元素的值value放入数组中下标为back的位置
  3. 放入元素后back++为下一次入队操作做准备
  4. 最后为了形成真正意义上的循环队列,需要利用%判断back的值是否需要从头开始,比如如果此时back的值等于开辟的空间数量,即back % (k+1) == 0,证明此时back需要从头开始了,若back<k+1,由于取模运算的性质back的值并不会发生改变,而back的值不会大于k+1 
//入队操作,失败返回假,成功返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))return false;obj->a[obj->back] = value;obj->back++;obj->back %= (obj->k+1);//上两步操作相当于:obj->back =(obj->back+1) % (obj->k+1);return true;
}

5、出队操作

  1. 先判断队列是否为空,若空则返回false
  2. 若不为空则将front的值++,即front表示的下标的值增大(->的优先级大于++)
  3. 最后为了形成真正意义上的循环队列,需要利用%判断front的值是否需要从头开始,比如如果此时front的值等于开辟的空间数量,即front % (k+1) == 0,证明此时front需要从头开始了,若front<k+1,由于取模运算的性质front的值并不会发生改变,而front的值不会大于k+1
//出队操作,失败返回假,成功返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return false;++obj->front;obj->front %= (obj->k+1);return true;
}

6、获取队头元素

  1. 先判断队列是否为空,若空则返回-1(题目要求此时应返回-1)
  2. 若不为空则直接返回下标值为front的数组元素
//获取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}

7、获取队尾元素

//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;//最初版本://return obj->a[(obj->back - 1 + obj->k + 1) % (obj->k+1)];//简化版本:return obj->a[(obj->back + obj->k) % (obj->k+1)];
}
  1.  先判断队列是否为空,若空则返回-1(题目要求此时应返回-1)
  2. 使用back-1是因为back表示的下标的前一个下标才是真正的队尾元素下标,而当back等于0时back-1等于-1,此时队尾元素的下标为4,为了获得该实际下标就需要进行后续的加和取模操作(如果back的值大于零则后面的加和取模运算并不会对实际要获取的下标值产生影响)总之,后续的加法和取模运算都是为了处理back等于0这一特殊情况的

8、销毁循环队列

//销毁队列
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}
  1. 为谁开辟了空间就销毁谁(平平无奇的销毁操作)

!!注意队列的判空和判满函数需要写在初始化队列的后面,否则报错!!

最终代码 

typedef struct {int* a;int front;  //front和back均表示下标int back;int k;      //这里k我们表示
} MyCircularQueue;//初始化循环队列
MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->a = (int*)malloc(sizeof(int)*(k+1));obj->front = 0;obj->back = 0;obj->k = k;return obj;
}//判断循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->back;
}//判断循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->back+1) % (obj->k+1) == obj->front;
}//入队操作,失败返回假,成功返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))return false;obj->a[obj->back] = value;obj->back++;obj->back %= (obj->k+1);return true;
}//出队操作,失败返回假,成功返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return false;++obj->front;obj->front %= (obj->k+1);return true;
}//获取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->front];
}//获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;//最初版本://return obj->a[(obj->back - 1 + obj->k + 1) % (obj->k+1)];//简化版本:return obj->a[(obj->back + obj->k) % (obj->k+1)];
}//销毁队列
void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}/*** Your MyCircularQueue struct will be instantiated and called as such:* MyCircularQueue* obj = myCircularQueueCreate(k);* bool param_1 = myCircularQueueEnQueue(obj, value);* bool param_2 = myCircularQueueDeQueue(obj);* int param_3 = myCircularQueueFront(obj);* int param_4 = myCircularQueueRear(obj);* bool param_5 = myCircularQueueIsEmpty(obj);* bool param_6 = myCircularQueueIsFull(obj);* myCircularQueueFree(obj);
*/

~over~

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

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

相关文章

问题 R: 胜利大逃亡(HUST)

#include <deque> #define inf 200000 #include<iostream> #include<queue> using namespace std;// 迷宫坐标 int map[59][59][59] { 0 };// 可访问标记 int visit[51][51][51] { 0 }; // 移动方式 int next1[7][4] { {1,0,0},{-1,0,0}, {0,1,0},{0,-1,…

【Spring】使用xml配置AOP

文章目录 1.前言2.xml配置AOP3. 总结 1.前言 在之前的学习中,都是使用注解的方式进行AOP的配置.其实使用xml配置文件也可以配置AOP. 2.xml配置AOP xml配置AOP方法如下: 添加相关依赖 <dependencies><dependency><groupId>org.springframework</groupId…

解决Zotero不显示标签的问题

目录 问题描述 解决办法&#xff1a; 问题描述 Zotero是一款学习助手&#xff0c;可以帮助我们梳理文献&#xff0c;方便我们整理。 最近电脑从windows换到mac&#xff0c;重新安装了Zotero&#xff0c;发现之前的一直设置都没有了。比如设置好的标签信息不显示了。如下图: …

pygame播放视频并实现音视频同步

一、前言 在我接触pygame时最新的pygame已经不支持movie模块&#xff0c;这就导致在pygame播放视频变成一个问题&#xff0c;网上搜了下解决方案有两个&#xff1a; 一是使用opencv播放视频&#xff0c;再结合pygame.mixer来播放音频 二是使用moviepy播放视频&#xff0c;再…

wsl安装ubuntu的问题点、处理及连接

WSL安装Ubuntu的参考链接 (41条消息) wsl报错&#xff1a;WslRegisterDistribution failed with error: 0x800701bc_yzpyzp的博客-CSDN博客_0x800701bc wsl (41条消息) 使用Ubuntu安装软件出现Unable to locate package错误解决办法_大灰狼学编程的博客-CSDN博客 手把手教你…

外贸ERP系统是什么?推荐的外贸管理软件?

外贸ERP管理系统有哪些&#xff1f;海洋建站管理软件的功能&#xff1f; 为了更有效地处理外贸业务&#xff0c;许多企业正在寻找先进的工具和技术。为了提高效率、降低成本并增强竞争力&#xff0c;越来越多的外贸企业正在转向外贸ERP系统。那么&#xff0c;外贸ERP系统究竟是…

抽象工厂设计模式是什么?什么是 Abstract Factory 抽象工厂设计模式?Python 抽象工厂设计模式示例代码

什么是 Abstract Factory 抽象工厂设计模式&#xff1f; 抽象工厂设计模式是一种创建型设计模式&#xff0c;旨在提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定其具体类。它允许客户端使用抽象的接口创建一组相关对象&#xff0c;而无需关注实际的对象实…

VSCode任务tasks.json中的问题匹配器problemMatcher的问题匹配模式ProblemPattern详解

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 一、简介 在 VS Code 中&#xff0c;tasks.json 文件中的 problemMatcher 字段用于定义如何解析任务输出中的问题&#xff08;错误、警告等&#xff09;。 problemMatcher有三种配置方式&#xff0c;具体可…

Java操作redis常见类型数据存储

目录 一、Java连接Redis 1.1 导入pom依赖 1.2 建立连接 二、Java使用Redis 2.1 字符串 String 2.2 哈希 Hash 2.3 列表 List 2.4 集合 Set 2.5 有序集合 Sorted Set 三、Redis的实际应用场景 一、Java连接Redis redis与mysq都是数据库&#xff0c;java操作redis其实跟…

2023年【施工升降机司机(建筑特殊工种)】最新解析及施工升降机司机(建筑特殊工种)考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 施工升降机司机(建筑特殊工种)最新解析参考答案及施工升降机司机(建筑特殊工种)考试试题解析是安全生产模拟考试一点通题库老师及施工升降机司机(建筑特殊工种)操作证已考过的学员汇总&#xff0c;相对有效帮助施工升…

SAP 快速Debug财务替代GGB1

本文目的是提供快速debug财务替代的步骤。 1.GGB1打开财务替代界面 2.找到需要调试的替代&#xff0c;并在tcode输入框中输入SHCB,回车后将显示系统自动生成的源码界面。 3.找到需要调试的步骤&#xff0c;设置断点&#xff0c;后续进行重现操作即可进入断点位置。 以上。

风电场数字孪生-升压站BIM三维模型-obj格式

简介&#xff1a; 风电场中的升压站三维模型&#xff0c;obj格式&#xff0c;采用BIM技术建模&#xff0c;可应用于风电场三维数字孪生领域&#xff0c;用于对升压站进行漫游浏览&#xff1b;三维可视化场景应用&#xff1b;风电场三维设计模型。 下载地址 风电场数字孪生-升…

PostgreSQL中所的锁

为了确保复杂的事务可以安全地同时运行&#xff0c;PostgreSQL提供了各种级别的锁来控制对各种数据对象的并发访问&#xff0c;使得对数据库关键部分的更改序列化。事务并发运行&#xff0c;直到它们尝试获取互相冲突的锁为止(比如两个事务更新同一行时)。当多个事务同时在数据…

java springboot在测试类中构建虚拟MVC环境并发送请求

好 上文java springboot在测试类中启动一个web环境我们在测试类中搭了一个web环境 那么 下面就要想办法弄一个接口的测试 这边 我们还是要在controller包下去创建一个 controller类 写一个访问接口 这里 我创建一个 TestWeb.java 这里 我们编写代码如下 package com.example.…

7.0 异常处理

1. 异常概述 1.1. 异常的概念 Java中的异常是指Java程序在运行时可能出现的错误或非正常情况&#xff0c;比如在程序中试图打开一个根本不存在的文件&#xff0c;在程序中除0等。异常是否出现&#xff0c;通常取决于程序的输入、程序中对象的当前状态以及程序所处的运行环境。…

Week-T10 数据增强

文章目录 一、准备环境和数据1.环境2. 数据 二、数据增强&#xff08;增加数据集中样本的多样性&#xff09;三、将增强后的数据添加到模型中四、开始训练五、自定义增强函数六、一些增强函数 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f…

查看文件的二进制数据

有时候会遇到想查看一些文件的二进制的数据的需求&#xff0c;比如想看一张图片的二进制数据&#xff0c;想查看bin文件的二进制数据&#xff0c;或者想查看其它文件的二进制数据等等。 在linux和mac下有命令直接支持&#xff0c;比较方便&#xff0c;但是很多人用的是windows…

【Java 进阶篇】JavaScript JSON 语法入门:轻松理解数据的序列化和反序列化

嗨&#xff0c;亲爱的小白们&#xff01;欢迎来到这篇关于 JavaScript 中 JSON&#xff08;JavaScript Object Notation&#xff09;语法的入门指南。JSON 是一种轻量级的数据交换格式&#xff0c;广泛应用于前端开发中。通过这篇博客&#xff0c;我将带你深入了解 JSON 的语法…

[userfaultfd] 2019-BalsnCTF_KrazyNote

前言 题目不算难, 但是这代码逆向可逆死个人:) 悲悲悲 程序分析 内核版本: v5.1.9 保护: 开了 kaslr, smep, smap. 现在的题目基本都开了, 都不用看. 其中 note 模块中注册了一个 misc 设备, 其函数表中就只有 note_open 和 note_unlocked_ioctl 两个函数, 其中 note_open…

C#入门(13):特性Attribute

C# 特性&#xff08;Attributes&#xff09;是用于在运行时为程序元素&#xff08;如类、方法、属性等&#xff09;添加声明性信息的一种方式。这些信息可以在程序运行时通过反射&#xff08;Reflection&#xff09;访问。特性可以用来控制程序行为、添加元数据或者影响程序的运…