设计循环队列-C语言实现

news2024/11/23 0:52:47

题目描述

设计循环队列

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

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

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

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

思路讲解

这道题目的意思就是设计一个循环队列,队列的大小是固定的,但可以一直排队插入数据,如果队列空间满了就排队等在后面,队列释放出了空间之后,又可以插入新的数据,就像一个固定的内存空间,可以插入数据在数据满的时候也可以删除数据,循环使用。
这里我们用数组的形式来设计一个循环队列,为什么要用数组来设计循环队列,而不用链表,那是因为,数组比链表更加方便访问,而链表操作起来会更加复杂一些,我们这里的空间是固定的,而不是随机的,开辟数组就相当于开辟了内存中可以连续存放的空间,访问数据的速度和链表相比更快。
我们这里最主要考虑的问题就是,在什么时候判断队列里面的数据存放满了,还有什么时候队列是空的
我们来画图看看
在这里插入图片描述
现在我们往里面插入数据,采用尾插,头不动
在这里插入图片描述
这样我们的数据就插满我们的队列了,我们首先把head 和tail都置为0,tail指向存放元素的下一个位置当tail把6插入完的时候tail已经完成回绕回到开头了就是head这个位置,发现当head == tail的时候我们的队列就插入满了
现在我们来删除元素,动head,tail不动。
在这里插入图片描述
现在发现删除元素删完之后,也是tail == head,这时候问题就来了,到底何时队列存满了数据,还有何时队列是空的,这里我们提出了一个解决方法,再在数组后面新添加一个空间,这个新添加的空间就是用来解决,队列判满这种情况的,这样我们可以把判满的条件改成head == tail+1
在这里插入图片描述
为什么这样可以解决问题,tail始终是指向有元素的下一个位置,当我们的插入满了的时候,我们的tail就指向了新开的空间,新开的空间指向的下一个就要回绕到开头,也就是头节点,所以 当tail +1 == head的时候队列就插入满了,而删除还是 当head == tail 不受影响。
当想明白这个问题之后,我们就可以开始写代码了,

代码分析

typedef struct {
    int* data;
    int head;
    int tail;
    int k;
} MyCircularQueue;

首先创建我们的整体结构,然后根据题意创建我们的队列

MyCircularQueue* myCircularQueueCreate(int k) {

MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
 obj-> data = (int*)malloc(sizeof(int)*(k+1));//k+1就代表新增的一个空间
 obj-> head = 0;
 obj-> tail = 0;
 obj->k = k;
 return obj;
}

这里力扣给的obj的定义是这样的
在这里插入图片描述

到这我们的队列就初始化完成了
下面是插入

插入队列

//插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))//调用判满函数,如果判满就返回false
    {
        return false;
    }
    else
    {
        obj->data[obj->tail] = value;//在尾巴后面插入
        obj->tail++;//尾巴位置++
        obj->tail = obj->tail%(obj->k+1);
        return true;
    }
}

这里我们主要说一下这行代码

   obj->tail = obj->tail%(obj->k+1);

这里主要是判断回绕,如果不采取判断何时回绕的话,数组就越界了,这里的设计很巧妙,采用取模的方式来判断
在这里插入图片描述
这里我们可以看到,当队列空间存放满的时候tail就指向了新开的空间tail就等于6,但插入完成之后ail本身也要++,就变成7,而K+1本身就等于7,7%7 = 0;就返回到数组开头了,完成了回绕,如果tail小于K+1,对K+1取模,就等于tail本身,这时就代表不需要回绕

删除队列

//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))//删除前要判空
    {
        return false;
    }
    else
    {
       obj->head++;//删除直接向前覆盖就行了
       obj->head = obj->head%(obj->k+1);
       return true;
    }
}

删除也和插入一样需要判断回绕问题,同样也是采用取模的方式判断

判满

下面是代码

bool myCircularQueueIsFull(MyCircularQueue* obj) {

       return (obj->tail+1)%(obj->k+1) == obj->head;
}

判满也是采用取模的方式进行的,我们还是拿这张图来看
在这里插入图片描述
这里的tail在插入最后一个数据之后变成7,7去%k+1,就是0,而0就是head的位置,就代表队列满了,所以通过这个表达式就可以判满。

取队头

/拿头
int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->data[obj->head];
    }
}

这里就没什么好说的了,就先判断队列是不是空的,然后返回头就行了。

取队尾数据

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->tail == 0? obj->data[obj->k]:obj->data[obj->tail-1];
    }
}

这里最主要的就是这行代码

return obj->tail == 0? obj->data[obj->k]:obj->data[obj->tail-1];

这里主要判断tail有两种情况,一种是普通的,tail就在队列中间,就像这样,这种我们就直接返回tail-1就行了
在这里插入图片描述
还有一种情况是tail完成回绕之后处于0处,这种如果直接减一就变成负数了,就像下面这张图
在这里插入图片描述
这里当tail == 0的时候我们就直接让他返回队列的最后一个元素下表位置也就是k。

判空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->tail == obj->head;
}

这里就直接可以看tail和head相等不相等。

释放空间

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->data);
    free(obj);
}

这里注意不能直接把obj释放了,要先释放他指向的那个数组,在释放它本身

源码

typedef struct {
    int* data;
    int head;
    int tail;
    int k;
} MyCircularQueue;
//要前置声明一下
MyCircularQueue* myCircularQueueCreate(int k);
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);
bool myCircularQueueDeQueue(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
int myCircularQueueFront(MyCircularQueue* obj);
int myCircularQueueRear(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {

MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
 obj-> data = (int*)malloc(sizeof(int)*(k+1));
 obj-> head = 0;
 obj-> tail = 0;
 obj->k = k;
 return obj;
}
//插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->data[obj->tail] = value;
        obj->tail++;
        obj->tail = obj->tail%(obj->k+1);
        return true;
    }
}
//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else
    {
       obj->head++;
       obj->head = obj->head%(obj->k+1);
       return true;
    }
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) {

       return (obj->tail+1)%(obj->k+1) == obj->head;
}
//拿头
int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->data[obj->head];
    }
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->tail == 0? obj->data[obj->k]:obj->data[obj->tail-1];
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->tail == obj->head;
}

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->data);
    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);
*/

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

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

相关文章

算法-卡尔曼滤波之为什么要使用卡尔曼滤波器

假设使用雷达来预测飞行器的位置; 预先的假设条件条件: 1.激光雷达的激光束每5s发射一次; 2.通过接受的激光束,雷达估计目标当前时刻的位置和速度; 3.同时雷达要预测下一时刻的位置和速度 根据速度,加速度和位移的…

Spring简介IOCDI

文章目录 Spring简介Spring课程介绍为什么要学学什么怎么学 初识SpringSpring家族Spring发展史 Spring体系结构Spring核心概念目前代码存在的问题核心概念 IOC和DI入门案例IOC入门案例入门案例分析实现步骤实现代码 DI入门案例DI入门案例分析实现步骤实现代码图解演示 Bean的基…

【超详细】跑通YOLOv8之深度学习环境配置3-YOLOv8安装

环境配置3下载安装内容如下: 1、配置清华等镜像源 2、创建环境 3、下载安装Pytorch 4、下载安装YOLOv8运行环境 版本:Python3.8(要求>3.8),torch1.12.0cu113(要求>1.8) 1、配置清华等镜…

用Arm CCA解锁数据的力量

安全之安全(security)博客目录导读 目录 CCA将如何改变Arm架构呢? 在实践中部署CCA 释放数据和人工智能的全部力量和潜力 早期计算中最大的挑战之一是管理计算资源,以最大化计算效率同时提供给不同程序或用户分配资源的分离。这导致了我们今天大多数使用的时间…

Windows环境下代码文档生成工具Doxygen使用详细教程

背景 最近研究aom源码,发现编译需要依赖Doxygen工具,故此篇博客详细记录下Doxygen的安装和使用。 Doxygen Doxygen 是一个强大的源代码文档生成工具,它支持多种编程语言,能够直接从源代码中的注释提取文档,并生成多…

基于SpringBoot设计模式之创建型设计模式·抽象工厂模式

文章目录 介绍开始架构图(以穿搭举例)样例一(html关于列表和表格的应用)定义抽象工厂(html)定义抽象工厂需要制作抽象产物(托盘)定义具体工厂(列表、表格)定义…

Mamba:4 魔幻矩阵A

若在阅读过程中有些知识点存在盲区,可以回到如何优雅的谈论大模型重新阅读。另外斯坦福2024人工智能报告解读为通识性读物。若对于如果构建生成级别的AI架构则可以关注AI架构设计。技术宅麻烦死磕LLM背后的基础模型。 ​Mamba自从出道就一直被拿来和Transformer对比…

HIVE卡口流量需求分析

HIVE卡口流量需求分析 目录 HIVE卡口流量需求分析 1.创建表格 插入数据 2.需求 3.总结: 1.创建表格 插入数据 CREATE TABLE learn3.veh_pass( id STRING COMMENT "卡口编号", pass_time STRING COMMENT "进过时间", pass_num int COMMENT …

懒人网址导航源码v3.9

测试环境 宝塔Nginx -Tengine2.2.3的PHP5.6 MySQL5.6.44 为防止调试错误,建议使用测试环境运行的php与mysql版本 首先用phpMyAdmin导入数据库文件db/db.sql 如果导入不行,请直接复制数据库内容运行sql语句也可以 再修改config.php来进行数据库配置…

AI智能体|我把Kimi接入了个人微信

大家好,我是无界生长。 最近加入AI学习交流群的小伙伴越来越多,我打算在微信群接入一个聊天机器人,让它协助管理微信群,同时也帮忙给群友解答一些问题。普通的群聊机器人肯定是不能满足需求的,得上AI大模型&#xff0c…

EPS软件标注点坐标值

1、如下,点击右侧(尺寸标注)按钮: 2、弹出一个对话框,如下: 3、在上图对话框中设置好箭头样式和小数位数,然后点击图上一点,右击结束再鼠标指定位置,如下: 如…

同城预约上门服务家政小程序

基于Thinkphp和原生微信小程序开发的一款同城预约、上门服务、到店核销家政系统,用户端、服务端、门店端各端相互依赖又相互独立,支持选择项目、选择服务人员、选择门店多种下单方式,支持上门服务和到店核销两种服务方式,支持自营…

树莓派|连接CSI接口摄像头+opencv

CSI(Camera Serial Interface)接口摄像头是一种常见的嵌入式系统或移动设备中使用的摄像头接口。它通常用于与处理器或图像传感器进行直接连接,实现高速的图像数据传输。 CSI接口摄像头具有以下特点: 高速传输:CSI接口…

仓库管理流程详解(附作业流程图)

仓库管理流程在企业的日常运营中至关重要。它不仅是物资流转的核心环节,更关乎着企业的运营效率、成本控制和客户服务水平。一个高效、规范的仓库管理流程能够确保货物从入库到出库的各个环节有序进行,减少资源浪费和时间成本,同时帮助企业实…

pdf怎么标注红色方框?五种PDF标注红色方框方法

pdf怎么标注红色方框?在当今数字化时代,PDF文档已成为我们日常工作和学习中不可或缺的一部分。然而,如何在海量的PDF文件中快速、准确地标注出重要信息,让内容更加醒目呢?今天,我将向大家介绍五种PDF标注红…

锁和MVCC如何实现mysql的隔离级别

概述 MVCC解决读的隔离性,加锁解决写的隔离性。 读未提交 读未提交,更新数据大概率使用的是独享锁吧。 读已提交 在 Read Committed(读已提交)隔离级别下,每次执行读操作时都会生成一个新的 read view。这是因为在读…

【数据结构】-- 相交链表-环形链表

交叉链表 . - 力扣(LeetCode) 如果链表的两条链的长度一样,链表两端对齐,解决这个问题将会变得非常简单,直接分别遍历两个链表,想等时的节点即为所求。我们想办法让链表对齐--分别从a和b遍历链表&#xff…

VRRP虚拟路由器冗余协议

VRRP概述 VRRP是什么 VRRP:虚拟路由器冗余协议过把几台路由设备联合组成一台虚拟的路由设备,将虚拟路由设备的IP地址作为用户的默认网关实现与外部网络通信当网关设备发生故障时,VRRP能够选举新的网关设备承担数据流量,从而保障…

2024年最新青龙面板跑脚本教程(一)持续更新中

文章目录 步骤 1: 安装青龙面板步骤 2: 访问青龙面板步骤 3: 上传或创建JavaScript脚本步骤 4: 添加定时任务步骤 5: 查看日志示例脚本步骤 6: 管理依赖和环境变量通用依赖如下,可手动增加。 要在青龙面板上运行JavaScript脚本,首先需要确保你已经成功安…

QCC---Aptx Lossless验证

因为aptx Lossless属于高通骁龙声音的一部分,一般支持高通骁龙声音的设备会支持到,比如说手机,而且还要支持最新的aptx adaptive协议R2.2版本。但是如果手上没有这样的手机的话,有source芯片也可以去做测试验证。在最新的784.1版本…