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

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

但是理解之后你会发现,嘛い---,也就那么个回事嘛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.写这条题目时顺便把自己的思路写了一下整理成笔记,分享一下

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

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

相关文章

CAN总线中隐性与显性问题

初学CAN总线时,对于CAN总线中的隐性为逻辑“1”,显性为逻辑“0”的疑惑一直令人困扰。在工控应用中,我们通常将有电压信号称为逻辑“1”,没有电压信号称为逻辑“0”,这与CAN总线的定义看起来不太一致。下面对这个问题进…

第1章:绪论 1.1数据库系统概述

文章目录 1.1 数据库系统概述1.1.1 数据库的4个基本概念1.1.2 数据管理技术的产生和发展1.1.3 数据库系统的特点 1.1 数据库系统概述 1.1.1 数据库的4个基本概念 数据(Data) 是数据库中存储的基本对象 数据的定义:描述事物的符号记录 数据的种类:文本、…

RabbitMQ(控制台模拟收发消息与数据隔离)

1.RabbitMQ架构图 publisher:生产者,也就是发送消息的一方 consumer:消费者,也就是消费消息的一方 queue:队列,存储消息。生产者投递的消息会暂存在消息队列中,等待消费者处理 exchange&…

基于java的宠物常规护理知识管理系统

项目源码:https://gitee.com/oklongmm/biye2 在设计一个宠物常规护理知识管理系统时,我们需要考虑系统的可扩展性,易用性和稳定性。以下是系统设计的功能模块: 一、用户模块: 1. 注册与登录:用户可以通过…

【面试题】webpack的五大核心、构建流程、性能优化

【面试题】webpack的五大核心、webpack的构建流程、webpack的性能优化 webpack是什么?webpack的五大核心webpack的构建流程webpack性能优化 webpack是什么? js静态模块打包工具。 功能 将多个文件打包成更小的文件,(压缩)翻译 babal-loader es6进行降级兼容。 …

HarmonyOS—开启AOT编译模式

AOT(Ahead Of Time)即提前编译,能够在Host端(即运行DevEco Studio的电脑)将字节码提前编译成Target端(即运行应用的设备)可运行的机器码,这样字节码可以获得充分编译优化&#xff0c…

Geeker Admin添加若以分离版本的后台作为后台

添加验证码 下载若依赖前后端分离版本,配置好自己数据库,redis连接地址 登录添加验证码 配置自己的若依后端连接地址 添加验证码请求方法 登录页面登录输入框添加验证码,uuid,调用的验证码刷新方法 注意:这里要用响应式定义验证…

CTP-API开发系列之各版本更新说明(持续更新)(值得收藏)

CTP-API开发系列之各版本更新说明(持续更新)(值得收藏) CTP-API开发系列之各版本更新说明(持续更新)(值得收藏)v6.7.2v6.7.1v6.7.0(推荐,主用)v6.…

Crossover24版现已上线!附免费升级攻略 Crossover软件下载使用方法

好久不见啦,最近一直在忙着研究Mac玩游戏,什么幻兽帕鲁、女神异闻录之类的,有些沉迷了,实在对不住大家… 不过今天还是给大家带来了好消息!那就是让Mac玩游戏不再是笑话的神器,CodeWeavers公司正式发布了C…

禅道:提bug、管理case 7.0

一、禅道的介绍 (1)定义禅道是一个项目管理工具,也是一个bug管理工具,还是一个用例管理工具。 (2)作用:为了解决众多企业在管理中出现混乱,无序的现象,开发出来 &…

QPainter::translate: Painter not active

画笔关联画布 就是这里少写了this指针