当前位置: 首页 > news >正文

循环队列:一道使数据结构萌新知道什么是“愁滋味“的题目

这破题目肝了我一天半才搞明白,也正是因为这道题目,我才豁然明白了李煜所说的"剪不断,理还乱...别是一般滋味在心头"到底是什么"滋味".在完全搞明白之前,真的是放有放不下,理也理不清...

但是理解之后你会发现,嘛い---,也就那么个回事嘛O(∩_∩)O

目录

1.题目:来自力扣

2.为什么选择数组(顺序表)是实现这道题的最好的方案

3. 有如何判断环状顺序表何种情况下为空,以及何种情况下为满的问题

4.需要实现的操作

5.具体操作的代码实现

5.1创建一个顺序表

5.2判空与判满操作

 5.3队尾加入新元素操作

 5.4队头删除元素操作

5.5显示头操作

5.6重难点:显示尾操作

6.写这条题目时顺便把自己的思路写了一下整理成笔记,分享一下


1.题目:来自力扣

2.为什么选择数组(顺序表)是实现这道题的最好的方案

  1. 简单性和易实现性:循环队列的实现相对简单,主要涉及数组的索引计算和循环移位。而链表的实现需要处理节点的创建、删除和指针操作,相对复杂
  2. 时间复杂度:在大多数情况下,循环队列的入队、出队操作的时间复杂度为O(1),因为只需要计算索引和进行简单的赋值操作。而链表的入队、出队操作可能涉及到节点的创建、删除和指针调整,时间复杂度为O(1)或O(n)。

3. 有如何判断环状顺序表何种情况下为空,以及何种情况下为满的问题

若空:则rear和front⼀定是指向同⼀位置的

若满:因为是循环顺序表(数组)那么理论上rear和front也是 .指向同⼀位置,但是这样便会与判空混淆,是⽆法操作的

这种问题我们称沩「假溢出」

所以,解决此问题的最佳方案便是⽤多额外开辟⼀个空间来解决假溢出问题

为确保队满时rear+1可以指向front,所以每次向队列中添加⼀个元素后rear 要指向当前元素的下⼀个位置,于是便有了rear+=1,然后顺理成章便有了 reart1=front来进⾏判断的代码.

如上图,如果rear到达循环队列最后⼀个位置时, rear=7,rear+1=8,但front =0,这并不符合我们的预期,故⽽我们设了⼀个值K,⽤于表示此循环队列中点共有多少个元素(这其中 包括为了解决假溢出⽽额开辟的空间)

⽤(rare+1)%(k+1)来控制rare范围

ps: k是怎么来的?先⽤malloc开辟整数倍的数组每个元素⼤⼩的空间 此时为k,再realloc对其进⾏扩展⾄(k+1),多⼀个假溢出空间。

4.需要实现的操作

bool myCircularQueueIsFull(MyCircularQueue* obj);//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判满
MyCircularQueue* myCircularQueueCreate(int k); //初始化
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);//入队
bool myCircularQueueDeQueue(MyCircularQueue* obj);//删除
int myCircularQueueFront(MyCircularQueue* obj);//显示头
int myCircularQueueRear(MyCircularQueue* obj);//显示尾
void myCircularQueueFree(MyCircularQueue* obj);//释放

5.具体操作的代码实现

5.1创建一个顺序表

typedef struct
{int* a;int front;int rear;int k;
} MyCircularQueue;
//使用malloc函数开辟环状顺序表所需空间
//realloc开辟额外空间结局假溢出问题
MyCircularQueue* myCircularQueueCreate(int k) 
{MyCircularQueue* obj0 = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));MyCircularQueue* obj = (MyCircularQueue*)realloc(obj0->a, (k + 1) * sizeof(MyCircularQueue));obj->front = 0;obj->rear = 0;obj->k = k;return obj;
}

5.2判空与判满操作

//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->front == obj->rear;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear + 1) % obj->k + 1 == obj->front;
}

 5.3队尾加入新元素操作

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

注:rear指向队尾元素的下⼀个位置

问题:循环数组最后⼀个空间是为了解决假栈溢出是不放元素的,如果数组 满了rare指向如图所示位置,再进⾏下⼀次放⼊元素的操作时它不就直接被 if(myCircularQueueIsFull(obj))驳回了 就不存在rare⽐k+1⼤的情况了 为啥 这⾥还要⽤obj->rear = (obj->rear) % obj->k + 1进⾏范围限制呢? 

回答:判断队列是否满和⽤取模限定范围这两个操作是⼆选⼀,但是为了 保证代码的健壮性,所以最好两个都写

 5.4队头删除元素操作

bool myCircularQueueDeQueue(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj)){return false;}free(obj->a[obj->front]);obj->front++;obj->front = (obj->front) % obj->k+1;//要考虑front在尾巴的情况return true;
}

注:如图:front在尾巴时,此时front为7,进⾏⼀次删除操作后,front会变成8,但是 这个循环队列没有下标为8的元素,不在范围内,我们预期的是此时front为0,所以 ⽤取模obj->front = (obj->front) % obj->k+1进⾏范围限制操作

5.5显示头操作

//显示头
int myCircularQueueFront(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj)){return -1;}else{return obj->a[obj->front];}
}

5.6重难点:显示尾操作

int myCircularQueueRear(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj)){return -1;}else{return obj->a[obj->rear - 1 + obj->k+1] % obj->k+1;}
}

解析:

因为rear是指向队尾下⼀个位置,所以只有rear-1才是队尾 rear!=0时rear-1是没有任何问题的

但是,如果rear=0时,rear-1=-1,不在这个范围内

所以这个问题要分为rear=0和rear!=0两种情况进⾏分类讨论

1.rear!=0时

直接是(rear-1)%(k+1),当A%B是,如果a[obj->rear - 1] % obj->k+1;

2.rear=0时

按循环链表的逻辑lai讲,rear-1=7,于是乎代数上,我们⽤rear-1+k-1=7, 但是如果rear不是0,⼜会发⽣超出范围的情况,我们可以%(k+1)

如此,将两种情况归纳⼀下,就是 return obj->a[obj->rear - 1 + obj- >k+1] % obj->k+1;

当然,如果嫌这么写太别扭,两种情况也可以⽤if...else语句拆开直⽩地 分情况写

6.写这条题目时顺便把自己的思路写了一下整理成笔记,分享一下

相关文章:

  • 字符串逆序
  • web坦克大战小游戏
  • Verilog参数、Verilog参数和属性冲突、整数处理
  • 【ArcPy】简化ArcGISPro默认Python环境体量
  • YOLOv8从入门到入土使用教程!(二)目标预测
  • QT使用FFMPEG库开发视频播放器
  • 惠普 DsekJet GT 5810/5820常见问题及解决方法
  • 低代码平台开发——基于React(文末送书)
  • MySQL相关问题
  • NLP_文本特征处理_4(代码示例)
  • 初级软件测试面试题
  • 计算机组成原理-第四章 指令系统【期末复习|考研复习】
  • Python与HTTP服务交互
  • Unix Network Programming Episode 88
  • Python 运算符介绍
  • 深入了解以太坊
  • Android 架构优化~MVP 架构改造
  • gf框架之分页模块(五) - 自定义分页
  • HashMap ConcurrentHashMap
  • IDEA 插件开发入门教程
  • python docx文档转html页面
  • TypeScript迭代器
  • vue 个人积累(使用工具,组件)
  • 从零搭建Koa2 Server
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 推荐一款sublime text 3 支持JSX和es201x 代码格式化的插件
  • 学习JavaScript数据结构与算法 — 树
  • 在weex里面使用chart图表
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #我与Java虚拟机的故事#连载01:人在JVM,身不由己
  • (+3)1.3敏捷宣言与敏捷过程的特点
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (23)Linux的软硬连接
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (原)本想说脏话,奈何已放下
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • .NET Core中Emit的使用
  • .NET 编写一个可以异步等待循环中任何一个部分的 Awaiter
  • .Net开发笔记(二十)创建一个需要授权的第三方组件
  • [2019/05/17]解决springboot测试List接口时JSON传参异常
  • [AIGC 大数据基础]hive浅谈
  • [BUUCTF NewStarCTF 2023 公开赛道] week3 crypto/pwn
  • [E单调栈] lc2487. 从链表中移除节点(单调栈+递归+反转链表+多思路)
  • [IT生活推荐]大家一起来玩游戏喽,来的都进!
  • [Linux]进程间通信(system V共享内存 | system V信号量)
  • [NAND Flash 6.1] 怎么看时序图 | 从时序理解嵌入式 NAND Read 源码实现
  • [NBIoT]NBIoT相关知识
  • [PyTorch][chapter 60][强化学习-2-有模型学习2]
  • [创业] 困难在克服之前是障碍, 克服之后就是壁垒
  • [第二章—Spring MVC的高级技术] 2.1Spring MVC配置的替代方案
  • [翻译] DSL和模型驱动开发的最佳实践(1/4)