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

文章目录

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

前言

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


一.循环队列(力扣)

. - 力扣(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.hqwc.cn/news/564365.html

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

相关文章

腾讯云服务器价格查询系统,2024年1年、3年和5年活动价格表

腾讯云服务器多少钱一年?61元一年起。2024年最新腾讯云服务器优惠价格表,腾讯云轻量2核2G3M服务器61元一年、2核2G4M服务器99元一年可买三年、2核4G5M服务器165元一年、3年756元、轻量4核8M12M服务器646元15个月、4核16G10M配置32元1个月、312元一年、8核…

Centos7 防火墙iptables?

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

【自编码器】梳理(上)

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 梳理有关自编码器 1. 自编码器 1.1 原理 Auto-Encoder,中文称作自编码器,是一种无监督式学习模型。利用输入数据 X X X本身作…

JUC(二)

1、wait notify Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态 BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片 BLOCKED 线程会在 Owner 线程释放锁时唤醒 WAITING 线程会在 Owner 线程调用 …

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

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

Vue2(十):全局事件总线、消息订阅与发布、TodoList的编辑功能、$nextTick、动画

一、全局事件总线!! 任意组件间通信 比如a想收到别的组件的数据,那么就在a里面给x绑定一个demo自定义事件,所以a里面就得有一个回调函数吧,然后我要是想让d组件给a穿数据,那就让d去触发x的自定义事件&…

【C++】如何用一个哈希表同时封装出unordered_set与unordered_map

👀樊梓慕:个人主页 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 🌝每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.哈希桶源码 2.哈希…

【算法】小强爱数学(迭代公式+数论取模)

文章目录 1. 问题2. 输入3. 输出4. 示例5. 分析6. 思路7. 数论,取模相关公式8. 数论,同余定理9. 代码 1. 问题 小强发现当已知 x y B xyB xyB以及 x y A xyA xyA时,能很轻易的算出 x n x_ {n} xn​ y n y_ {n} yn​ 的值.但小强想请你在已知A和B的…

vue3项目初始化

初始化项目newsapp VSCode 打开终端,newsapp项目目录,可自定义 vue create newsapp 有提示“因为在此系统上禁止运行脚本”的话,请执行 set-ExecutionPolicy RemoteSigned 执行后再重复执行vue create newsapp 注意选择Vue 3版本 测试项…

CSS案例-2.简单版侧边栏练习

效果 知识点 标签显示模式 块级元素 block-level 常见元素:<h1>~<h6>、<p>、<div>、<ul>、<ol>、<li>等。 特点: 独占一行长度、宽度、边距都可以控制宽度默认是容器(父级宽度)的100%是一个容器及盒子,里面可以放行内或者…

24计算机考研调剂 | 【官方】山东工商学院

山东工商学院 考研调剂招生信息 招生专业&#xff1a; 学院概况&#xff1a; 计算机科学与技术学院始建于1999年&#xff0c;拥有计算机科学与技术一级学科硕士点,在2022软科中国最好学科排名中&#xff0c;计算机科学与技术学科位列全国第104位。在2022年“软科”中国大学专…

ShardingSphere水平分表——开发经验(2)

1. 什么场景下分表&#xff1f; 数据量过大或者数据库表对应的磁盘文件过大。 Q&#xff1a;多少数据分表&#xff1f; A&#xff1a;网上有人说1kw&#xff0c;2kw&#xff1f;不准确。 1、一般看字段的数量&#xff0c;有没有包含text类型的字段。我们的主表里面是不允许有t…