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

文章目录

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

前言

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


一.循环队列(力扣)

. - 力扣(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,一经查实,立即删除!

相关文章

P1020 [NOIP1999 提高组] 导弹拦截

一、问题描述 P1020 [NOIP1999 提高组] 导弹拦截 二、问题简析 该题要我们求两个问题: 1、不上升子序列的最大长度2、不上升子序列的最少个数 利用 D i l w o r t h Dilworth Dilworth 定理,我们得到不上升子序列的最少个数等于上升子序列的最大长…

【算法每日一练]-动态规划(保姆级教程 篇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…

Ollama、Langchain相关学习资源(动态更新)

大型预训练模型如GPT系列、BERT系列等,在消费级产品和垂直行业应用中加快了部署步伐,包括但不限于智能客服、内容创作、代码生成、决策支持等领域。 随着大模型开源,相关的部署工具和框架也得到发展和完善。例如,出现了一些专门针…

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

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

基于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为控制点,曲线和初始与终止端点相切,并且…

Java解决作为子字符串出现在单词中的索引

Java解决作为子字符串出现在单词中的索引 01 题目 给你一个字符串数组 patterns 和一个字符串 word ,统计 patterns 中有多少个字符串是 word 的子字符串。返回字符串数目。 子字符串 是字符串中的一个连续字符序列。 示例 1: 输入:patterns…

【原理图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创建并激…

【string】查找最长的公共子序列(substr()/find())

实现一个算法查找两个字符串最长的公共子字符串。子字符串的介绍如下: 子字符串是指字符串中任意个连续的字符组成的子序列 输出一行,为最长公共子序列。 分析:使用substr()与find()函数 if(str2.find(temp)!string…

【论文精读】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制作属性面板(二)

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

01背包问题dp

01背包 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i件物品的体积是 vi,价值是 wi。 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。 输入格式 第一行两个整数,N…