LeetCode每日精进:622.设计循环队列

题目链接:622.设计循环队列

题目描述:

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

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1);  // 返回 true
circularQueue.enQueue(2);  // 返回 true
circularQueue.enQueue(3);  // 返回 true
circularQueue.enQueue(4);  // 返回 false,队列已满
circularQueue.Rear();  // 返回 3
circularQueue.isFull();  // 返回 true
circularQueue.deQueue();  // 返回 true
circularQueue.enQueue(4);  // 返回 true
circularQueue.Rear();  // 返回 4

提示:

  • 所有的值都在 0 至 1000 的范围内;
  • 操作数将在 1 至 1000 的范围内;
  • 请不要使用内置的队列库。

思路:

循环队列的逻辑结构

        以队列长度k为4示例:

循环队列的底层结构

        以队列长度k为4示例: 

        若用链表实现,那么就得使用环形链表实现循环结构

        对于用链表实现的循环队列,除了需要手动连环之外,与队列的实现相同,还需要定义两个结构体, 参考队列:数据结构中的“排队艺术”,一个用来定义链表的结点的结构,一个用来定义队头和队尾的结构。

        若用数组实现

        对于用数组实现的循环队列,只需让队尾rear越界后模上数组容量即可让rear再次回到队头,从而实现循环结构。 

        分析:这样对比来看,用数组实现循环队列只需定义一个结构体,更加便捷。

循环队列的结构

        以循环队列长度为4示例:

        如图可以看到,当队列为空时,队头front和队尾rear指向同一个位置,当队列已满时,队头front和队尾rear依然指向同一个位置,所以单单是这样通过队列长度申请对应数据个数的数组是无法区分当前队列是空队列状态还是队列已满的状态,所以需要重新设计数组结构。        

        当循环队列长度为k时,我们申请k+1个数据空间的数组:

        当队列为空,队头front和队尾rear指向同一个位置,即满足:

front == rear 

        当队列已满时,rear的下一个位置指向front,即满足:

( rear + 1 ) % ( k + 1 ) == front 

        那么这种结构就可以解决区分空队列和满队列的问题了。 

typedef struct {
    int* arr;
    int front;
    int rear;
    int capacity;
} MyCircularQueue;

         用capacity来记录队列当前的容量,front和rear分别指向队头和队尾。

创建循环队列

MyCircularQueue* myCircularQueueCreate(int k)

        首先,创建循环队列的结构体,申请k+1个数据空间的数组,将队头与队尾指向数组头部,并将队列容量设置为k。

代码实现;

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    pq->arr = (int*)malloc(sizeof(int)*(k+1));
    pq->front = pq->rear = 0;
    pq->capacity = k;
    return pq;
}

检查循环队列是否为空

        当队头和队尾指向同一位置时,循环队列为空,队列为空,返回true,队列不为空,返回false。

代码实现:

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front == obj->rear;
}

检查循环队列是否已满

        当队列已满时,rear的下一个位置指向front,满足上文推理的表达式:

 ( rear + 1 ) % ( k + 1 ) == front 

        队列已满,返回true,队列未满,返回false。 

代码实现:

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->capacity+1) == obj->front;
}

向循环队列插入一个元素

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }

         在插入元素之前,我们需要对检查队列是否已满,若队列已满,则直接返回false。               

obj->arr[obj->rear++] = value;

          若队列未满,则将元素添加到队尾位置,并让rear++。

obj->rear %= obj->capacity+1;

        为防止出现rear++后rear越界的情况,需要再将rear模上数组长度,使rear回到数组头部,使其下一个位置为front。

        插入成功,返回true。

完整代码:   

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

 从循环队列中删除一个元素

bool myCircularQueueDeQueue(MyCircularQueue* obj)
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }

        在删除元素前,需要判断队列是否为空,若队列为空。返回false

    obj->front++;

        若队列不为空,front++,队头移向后一个位置。

       

    obj->front %= obj->capacity+1;

        为防止front++后,front越界,让front也模上数组长度,让其重新回到数组头部。

        删除成功,返回true。

完整代码:

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front++;
    obj->front %= obj->capacity+1;
    return true;
}

从队首获取元素

int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arr[obj->front];
}

        检查队列是否为空,若队列为空,则返回-1,若队列不为空,返回数组中front下标对应的元素。

获取队尾元素

int myCircularQueueRear(MyCircularQueue* obj)
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }

        先检查队列是否为空,若队列为空,获取失败,则直接返回-1。

    int prev = obj->rear-1;

        若队列不为空,rear不位于数组头部,返回rear的前一个下标prev所储存的数据。

    if (obj->rear == 0)
    {
        prev = obj->capacity;
    }

        若rear位于数组头部时,capacity所指向的数据就是队尾元素,返回即可。

完整代码:

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    int prev = obj->rear-1;
    if (obj->rear == 0)
    {
        prev = obj->capacity;
    }
    return obj->arr[prev];
}

销毁 

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    obj->arr = NULL;
    free(obj);
    obj = NULL;
}

        销毁动态申请的数组后,由于参数obj接收了myCircularQueueCreate的返回值,所以也需要为其释放空间,置空。 

代码总览:

typedef struct {
    int* arr;
    int front;
    int rear;
    int capacity;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    pq->arr = (int*)malloc(sizeof(int)*(k+1));
    pq->front = pq->rear = 0;
    pq->capacity = k;
    return pq;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front == obj->rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->capacity+1) == obj->front;
}

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

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front++;
    obj->front %= obj->capacity+1;
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->arr[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    int prev = obj->rear-1;
    if (obj->rear == 0)
    {
        prev = obj->capacity;
    }
    return obj->arr[prev];
}


void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    obj->arr = NULL;
    free(obj);
    obj = NULL;
}

       

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网络安全学习-常见安全漏洞检测以及修复方法-1

渗*透测试 渗透测试就是模拟攻击者入侵系统,对系统进行一步步渗透,发现系统的脆弱环节和隐藏风险。形成测试报告提供给系统的所有者,所有者根据报告对系统进行加固,提升系统的安全性,防止真正的攻击者入侵。 渗透测试…

鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar)

鸿蒙开发深入浅出01(基本环境搭建、页面模板与TabBar) 1、效果展示2、下载 DevEco Studio3、创建项目4、新建页面模板5、更改应用信息6、新建以下页面7、Index.ets8、真机运行9、图片资源文件 1、效果展示 2、下载 DevEco Studio 访问官网根据自己的版本…

C/C++ | 每日一练 (4)

💢欢迎来到张胤尘的技术站 💥技术如江河,汇聚众志成。代码似星辰,照亮行征程。开源精神长,传承永不忘。携手共前行,未来更辉煌💥 文章目录 C/C | 每日一练 (4)题目参考答案基础容器序列容器std:…

(八)趣学设计模式 之 装饰器模式!

目录 一、 啥是装饰器模式?二、 为什么要用装饰器模式?三、 装饰器模式的实现方式四、 装饰器模式的优缺点五、 装饰器模式的应用场景六、 装饰器模式 vs 代理模式七、 总结 🌟我的其他文章也讲解的比较有趣😁,如果喜欢…

快节奏生活

在当今快节奏的商务环境中,效率成为了决定企业竞争力的关键因素之一。亿可达软件连接平台,以其独特的功能和优势,为职场人士带来了前所未有的便捷与高效,成为了众多用户心中的“宝藏”工具。 1、亿可达:自动化流程的搭…

Jenkins protoc: command not found

个人博客地址:Jenkins protoc: command not found | 一张假钞的真实世界 在使用Jenkins编译Hadoop3.1.2时报错信息如下: [INFO] --- hadoop-maven-plugins:3.1.2:protoc (compile-protoc) hadoop-common --- [WARNING] [protoc, --version] failed: j…

SOME/IP协议的建链过程

在SOME/IP协议中,建立服务通信链路的过程主要涉及服务发现机制,通常需要以下三次交互: 服务提供者广播服务可用性(Offer Service) 服务提供者启动后,周期性地通过Offer Service消息向网络广播其提供的服务实例信息(如Service ID、Instance ID、通信协议和端口等)。 作用…

考研/保研复试英语问答题库(华工建院)

华南理工大学建筑学院保研/考研 英语复试题库,由华工保研er和学硕笔试第一同学一起整理,覆盖面广,助力考研/保研上岸!需要👇载可到文章末尾见小🍠。 以下是主要内容: Part0 复试英语的方法论 Pa…

Linux7-线程

一、前情回顾 chdir();功能: 函数用于改变当前进程的工作目录。 参数:路径(Path):这是一个字符串参数,表示要切换到的目标目录的路径。 返回值: 成功:在成功改变当前工作目…

防火墙双机热备---VRRP,VGMP,HRP(超详细)

双机热备技术-----VRRP,VGMP,HRP三个组成 注:与路由器VRRP有所不同,路由器是通过控制开销值控制数据包流通方向 防火墙双机热备: 1.主备备份模式 双机热备最大的特点就是防火墙提供了一条专门的备份通道(心…

LabVIEW形状误差测量系统

在机械制造领域,形状与位置公差(GD&T)直接影响装配精度与产品寿命。国内中小型机加工企业因形状误差导致的返工率高达12%-18%。传统测量方式存在以下三大痛点: ​ 设备局限:机械式千分表需人工读数,精度…

本地部署大模型: LM Studio、Open WebUI 与 Chatbox 全面对比以及选型指南

1. 工具概述 LM Studio 定位:专注于本地化大模型实验与推理的桌面工具,支持多模型并行、Hugging Face集成及离线运行。 核心功能: 图形化界面直接加载GGUF模型文件,支持NVIDIA/AMD GPU加速。 内置OpenAI兼容API,可搭…

百度觉醒,李彦宏渴望光荣

文 | 大力财经 作者 | 魏力 2025年刚刚开年,被一家名为DeepSeek的初创公司强势改写。在量化交易出身的创始人梁文锋的带领下,这支团队以不到ChatGPT 6%的训练成本,成功推出了性能可与OpenAI媲美的开源大模型。 此成果一经问世,…

mysql 迁移到人大金仓数据库

我是在windows上安装了客户端工具 运行数据库迁移工具 打开 在浏览器输入http://localhost:54523/ 账号密码都是kingbase 添加mysql源数据库连接 添加人大金仓目标数据库 添加好的两个数据库连接 新建迁移任务 选择数据库 全选 迁移中 如果整体迁移不过去可以单个单个或者几个…

Spring Cloud — Hystrix 服务隔离、请求缓存及合并

Hystrix 的核心是提供服务容错保护,防止任何单一依赖耗尽整个容器的全部用户线程。使用舱壁隔离模式,对资源或失败单元进行隔离,避免一个服务的失效导致整个系统垮掉(雪崩效应)。 1 Hystrix监控 Hystrix 提供了对服务…

【链 表】

【链表】 一级目录1. 基本概念2. 算法分析2.1 时间复杂度2.2 空间复杂度2.3 时空复杂度互换 线性表的概念线性表的举例顺序表的基本概念顺序表的基本操作1. 初始化2. 插入操作3. 删除操作4. 查找操作5. 遍历操作 顺序表的优缺点总结优点缺点 树形结构图形结构单链表基本概念链表…

记录锁,间隙锁,Next-Key Lock

记录锁,间隙锁,Next-Key Lock mysql的锁机制一、InnoDB行锁的种类1、记录锁(Record Lock)(1)不加索引,两个事务修改同一行记录(2)不加索引,两个事务修改同一表…

vue3父子组件props传值,defineprops怎么用?(组合式)

目录 1.基础用法 2.使用解构赋值的方式定义props 3.使用toRefs的方式解构props (1).通过ref响应式变量&#xff0c;修改对象本身不会触发响应式 1.基础用法 父组件通过在子组件上绑定子组件中定义的props&#xff08;:props“”&#xff09;传递数据给子组件 <!-- 父组件…

鸿蒙Next-方法装饰器以及防抖方法注解实现

以下是关于 鸿蒙Next&#xff08;HarmonyOS NEXT&#xff09;中 MethodDecorator 的详细介绍及使用指南&#xff0c;结合了多个技术来源的实践总结&#xff1a; 一、MethodDecorator 的概念与作用 MethodDecorator 是鸿蒙Next框架中用于装饰类方法的装饰器&#xff0c;属于 Ark…

快速入门——状态管理VueX

Vuex介绍 状态管理 每一个Vuex应用的核心都是一个store&#xff0c;与普通的全局对象不同的是&#xff0c;基于Vue数据与视图绑定的特点&#xff0c;当store中的状态发生变化时&#xff0c;与之绑定的视图也会被重新渲染。 store中的状态不允许被直接修改&#xff0c;改变sto…