如何设计循环队列(两种方法)

文章目录

  • 前言
  • 一、方法一:数组法
  • 二、方法二.链表法
  • 总结

前言

前面有提到过队列的知识,这次来说一下怎么设计一个循环队列


一.循环队列(力扣)

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=N7T8https://leetcode.cn/problems/design-circular-queue/description/

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

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

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

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

思路:循环队列无论使用数组实现还是链表实现,都要多开一个空间,也就意味着要是存K个数据的循环队列,就要开K+1个空间,不然无法实现判空和判满

方法一:数组法

注意数组法的判空和判满

判空:就是front==tail的时候就是空的,判满:当(tail+1)%(k+1)==front就是满的

1.0初始化

初始化一个数组,有头front,尾tail,数组明a

typedef struct {int* a;int front;int tail;int k;} MyCircularQueue;

1.1创建

MyCircularQueue* myCircularQueueCreate(int k) 
{//开辟一个循环队列的空间MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//开辟一个数组的空间用来实现循环队列,要多开辟一个q->a = (int*)malloc(sizeof(int) * (k + 1));//初始化q->front = q->tail = 0;q->k = k;return q;
}

1.2判空,判满

判空:就是front==tail的时候就是空的,判满:当(tail+1)%(k+1)==front就是满的

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

1.3插入

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//如果循环队列是满的就不能插入if (myCircularQueueIsFull(obj))return false;//把末尾的值插入值obj->a[obj->tail] = value;//然后tail的往后走++obj->tail;//防止数组越界,%(k+1)把下标都控制在k之内//把越界的重置obj->tail %= (obj->k + 1);return true;
}

1.4删除

数组的删除不用向链表这些Pop,直接覆盖就可以了

//数组的删除不用向链表这些Pop,直接覆盖就可以了
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//如果是空的就不能删if (myCircularQueueIsEmpty(obj))return false;//头往前走++obj->front;//止数组越界,%(k+1)把下标都控制在k之内obj->front %= (obj->k + 1);return true;
}

1.5拿出最后一个数

int myCircularQueueRear(MyCircularQueue* obj) {//如果是空的就拿不了if (myCircularQueueIsEmpty(obj)){return -1;}//存在特殊情况,当tail为0时,尾才最后,所以不能直接拿出tail之前的数if (obj->tail == 0)return obj->a[obj->k];elsereturn obj->a[obj->tail - 1];
}

1.6拿出头数据和销毁

//直接拿出
int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}return obj->a[obj->front];}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}

1.7总代码

注意把判空判满提前引用!!!

typedef struct {int* a;int front;int tail;int k;} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);MyCircularQueue* myCircularQueueCreate(int k) 
{//开辟一个循环队列的空间MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//开辟一个数组的空间用来实现循环队列,要多开辟一个q->a = (int*)malloc(sizeof(int) * (k + 1));//初始化q->front = q->tail = 0;q->k = k;return q;
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//如果循环队列是满的就不能插入if (myCircularQueueIsFull(obj))return false;//把末尾的值插入值obj->a[obj->tail] = value;//然后tail的往后走++obj->tail;//防止数组越界,%(k+1)把下标都控制在k之内obj->tail %= (obj->k + 1);return true;
}//数组的删除不用向链表这些Pop,直接覆盖就可以了
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//如果是空的就不能删if (myCircularQueueIsEmpty(obj))return false;//头往前走++obj->front;//止数组越界,%(k+1)把下标都控制在k之内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;}//存在特殊情况,当tail为0时,尾才最后,所以不能直接拿出tail之前的数if (obj->tail == 0)return obj->a[obj->k];elsereturn obj->a[obj->tail - 1];
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->tail;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->tail + 1) % (obj->k + 1) == obj->front;
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}

方法二.链表法

2.0初始化

先初始化一个链表,在定义结构

typedef struct listNode {int data;struct Node* next;
}Node;typedef struct {Node* front;Node* tail;int k;
}MyCircularQueue;

2.1创建

这个是最难的部分,就是在创建的时候要创造一个循环链表,注意:这里其实已经开辟了k+1个空间了,不懂的自己画图

MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));cq->front = cq->tail = (Node*)malloc(sizeof(Node));cq->k = k;//创造一个循环链表//这里其实已经开辟了k+1个空间了注意while (k){Node* cur= (Node*)malloc(sizeof(Node));cur->data = 0;cur->next = NULL;cq->tail->next =cur;cq->tail= cq->tail->next;k--;}//开辟好了之后还要把尾和头发一起cq->tail->next =cq->front;cq->tail= cq->front;return cq;
}

2.2判空,判满

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->tail;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return obj->tail->next == obj->front;
}

2.3插入

//插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//先判满if (myCircularQueueIsFull(obj))return false;//直接在尾上插入obj->tail->data = value;obj->tail= obj->tail->next;return true;
}

2.4删除

//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//先判空if (myCircularQueueIsEmpty(obj))return false;//头删obj->front = obj->front->next;return true;
}

2.5去头元素

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

2.6去尾元素

注意尾是前一个元素,所以不可以直接拿出,实在不理解看一下直接的动图

int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;//去找尾Node* cur= obj->front;while (cur->next != obj->tail)cur= cur->next;return cur->data;}

2.7销毁

这个是我自己犯的错误

cur=cur->next,为什么不可以,因为cur等于头节点,cur等于cur->next,再释放cur,相当于把头节点next释放掉了,那我头节点后面的后面怎么去找呢?所以我们是从头节点开始释放的,把头节点用cur记录下来,释放之前让头节点走了,但是cur是头节点的傀儡节点,所以释放cur相当于是释放头节点了。

void myCircularQueueFree(MyCircularQueue* obj) {//和单链表的销毁一样Node* tmp = obj->front;while (obj->k + 1){//cur=cur->next;为什么不可以obj->front = obj->front->next;free(tmp);tmp = obj->front;obj->k--;}free(obj);
}

2.8总代码

注意把判空判满提前引用!!!

typedef struct listNode {int data;struct Node* next;
}Node;typedef struct {Node* front;Node* tail;int k;
}MyCircularQueue;bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));cq->front = cq->tail = (Node*)malloc(sizeof(Node));cq->k = k;//创造一个循环链表//这里其实已经开辟了k+1个空间了注意while (k){Node* cur= (Node*)malloc(sizeof(Node));cur->data = 0;cur->next = NULL;cq->tail->next =cur;cq->tail= cq->tail->next;k--;}//开辟好了之后还要把尾和头发一起cq->tail->next =cq->front;cq->tail= cq->front;return cq;
}
//插入
//他这个题目其实是提前开辟好了,让你直接插入就可以了
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//先判满if (myCircularQueueIsFull(obj))return false;//直接在尾上插入obj->tail->data = value;obj->tail= obj->tail->next;return true;
}
//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {//先判空if (myCircularQueueIsEmpty(obj))return false;//头删obj->front = obj->front->next;return true;
}int myCircularQueueFront(MyCircularQueue* obj)
{if(myCircularQueueIsEmpty(obj))return -1;return obj->front->data;
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;//去找尾Node* cur= obj->front;while (cur->next != obj->tail)cur= cur->next;return cur->data;}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->tail;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return obj->tail->next == obj->front;
}void myCircularQueueFree(MyCircularQueue* obj) {//和单链表的销毁一样Node* tmp = obj->front;while (obj->k + 1){obj->front = obj->front->next;free(tmp);tmp = obj->front;obj->k--;}free(obj);
}


总结

用两种解法理解了循环队列,想必对链表和队列的知识做到了巩固

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

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

相关文章

【算法每日一练]-动态规划(保姆级教程 篇17 状态压缩)#POJ1185:炮兵阵地 #互不侵犯

目录 今日知识点: 把状态压缩成j,dp每行i的布置状态,从i-1和i-2行进行不断转移 把状态压缩成j,dp每行i的布置状态,从i-1行进行状态匹配,然后枚举国王数转移 POJ1185:炮兵阵地 思路: 题目:互…

代码随想录算法训练营第二十八天|● 93.复原IP地址 ● 78.子集 ● 90.子集II (JS写法)

93 复原IP地址 题目链接/文章讲解:https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html 视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/ 思路: /*** param {string} s* return {string[]}*/ var resto…

微信商家转账到零钱:实用指南,涵盖开通、使用与常见问题

商家转账到零钱是什么? 商家转账到零钱功能整合了企业付款到零钱和批量转账到零钱,支持批量对外转账,操作便捷。如果你的应用场景是单付款,体验感和企业付款到零钱基本没差别。 商家转账到零钱的使用场景有哪些? 这…

基于YOLOv8深度学习的橙子病害智能诊断与防治系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分类

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

【动态规划】Leetcode 746. 使用最小花费爬楼梯

【动态规划】Leetcode 746. 使用最小花费爬楼梯 解法 ---------------🎈🎈题目链接🎈🎈------------------- 解法 😒: 我的代码实现> 动规五部曲 ✒️确定dp数组以及下标的含义 dp[i] 表示跳跃到第 i 层&#x…

「Swift」AttributedString常见使用方法

前言:AttributedString是Apple推出的可以实现单个字符或字符范围带相应属性的字符串。属性提供了一些文本特性,可以让文本展示的样式更加丰富。在日常开发过程中,我通常用于同一个Label中包含不同的字体大小或字体颜色的样式编写中。 使用举…

11 Games101 - 笔记 - 几何(曲线与曲面)

11 几何(曲线与曲面) 贝塞尔曲线 定义 贝塞尔曲线:由控制点和线段组成的曲线,控制点是可拖动的支点。 如图,蓝色为贝塞尔曲线,p1, p2, p3为控制点,曲线和初始与终止端点相切,并且…

【原理图PCB专题】Cadence 17.4版本导出excel版本坐标文件

坐标文件记录了板卡上每个元件的坐标是生产的基础资料,在PCBA生产时,需要提坐标文件并且务必保证 准确无误。 Cadence导出坐标文件大部分网上的都是txt文件,其格式如下。导出的文件存在没有对应的Title,格式打开混乱等问题。 那么Cadence 17.4版本如何导出有标题、…

分布式系统的基本特性

一般,分布式系统需要支持以下特性: 资源共享 开放性 并发性 可伸缩性 容错性 透明性 下面分别讨论。 容易理解的 资源共享 一旦授权,可以访问环境中的任何资源。 资源:包括硬件(e.g. printer, scanner, camera)、软件&a…

对于组件通信的深刻理解

父组件传递数据给子组件 props传递数据 父组件在子组件的标签上写自定义的属性,属性值是自己的变量,当渲染到子组件时,执行props会找自定义属性,内存了变量的内存,可访问到,写props,会生成vue实例的时候,将props的变量赋给,值找变量内存存入变量.插值语句等可访问.父组件会变…

python综合实战案例-数据分析

Python是进行数据分析的好工具,今天就是借助一个案例给大家进行数据分析讲解。 本例设计一个log.txt⽂件,该文件记录了某个项⽬中某个 api 的调⽤情况,采样时间为每分钟⼀次,包括调⽤次数、响应时间等信息,⼤约18万条数…

如何在 Django 中使用 pyecharts

为项目新建一个目录,将其命名为django_pyecharts_demo, 在终端中切换到这个目录,并创建一个虚拟环境。 python -m venv django_pyecharts激活虚拟环境 django_pyecharts\Scripts\activate要停止使用虚拟环境,可执行命令 deactivate创建并激…

【论文精读】MAE:Masked Autoencoders Are Scalable Vision Learners 带掩码的自动编码器是可扩展的视觉学习器

系列文章目录 【论文精读】Transformer:Attention Is All You Need 【论文精读】BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding 【论文精读】VIT:vision transformer论文 文章目录 系列文章目录一、前言…

24. UE5 RPG制作属性面板(二)

在上一篇中,我们创建属性面板的大部分样式,这一篇里面接着制作。 在这一篇里我们需要有以下几个方面: 在界面增加一个属性按钮。属性按钮增加事件,点击时可以打开属性面板,属性面板打开时无法再次点击按钮。点击属性面…

手撕算法-无重复字符的最长子串

描述 分析 滑动窗口,记录窗口中的所有出现的字符,然后窗口左边界固定,右边界右滑,如果,窗口中不存在新的字符,则右滑成功,否则左边界右滑,直到窗口中不存在右边界的值。 描述感觉不…

Centos7 防火墙iptables?

Centos7 防火墙iptables? 文章目录 Centos7 防火墙iptables?1. 介绍2. firewalld 和 iptables区别3. 区域管理概念区域管理有如下几种不同的初始化区域: 4.iptables的配置1.简述2.基本原理3.iptables传输数据包的过程4. iptables规则表和链5.…

结构体类型详细讲解(附带枚举,联合)

前言: 如果你还对结构体不是很了解,那么本篇文章将会从 为什么存在结构体,结构体的优点,结构体的定义,结构体的使用与结构体的大小依次介绍,同样会附带枚举与联合体 目录 为什么存在结构体: 结构…

【Linux 驱动基础】IMX6ULL LED基础驱动

本机使用的是正点原子的IMX6ULL开发板 # 前置知识 IMX6ULL GPIO控制框图: GPIO控制代码大概分为几个流程:开启时钟、设置IO复用、设置IO属性、配置IO方向、设置IO输出电平,下面以IMX6ULL为例, 1. 开启时钟 参考资料&#xff1a…

深入理解 Docker 镜像

1. Docker 镜像的底层原理 1.1 分层的镜像 以我们的pull 命令为例,在下载的过程中我们可以看到docker的镜像好像是一层一层的在下载。 1.2 UnionFS(联合文件系统) 联合文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次…

MPI4.1文档翻译(持续更新)

本博客参考官方文档进行介绍,全网仅此一家进行中文翻译,走过路过不要错过。 官方网址:https://www.mpi-forum.org/ 参考文档:https://www.mpi-forum.org/docs/mpi-4.1/mpi41-report.pdf 引用官方4.1文档方法: manu…