【初阶数据结构】——循环队列

文章目录

  • 1. 什么是循环队列?
  • 2. 结构的选择:数组 or 链表?
    • 链表结构分析
    • 数组结构分析
      • 判空判满
      • 入数据出数据
      • 取队头队尾元素
  • 3. 代码实现(数组结构)
    • C语言版本
    • C++版本

这篇文章我们来学习一下如何实现循环队列
那力扣上呢有一个对应的题我们可以来看一下:

1. 什么是循环队列?

在这里插入图片描述

循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
在这里插入图片描述
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
要求我们实现的循环队列要有以下几个接口:
在这里插入图片描述
在这里插入图片描述

2. 结构的选择:数组 or 链表?

那下面要实现循环队列的话,我们采用哪种结构呢,数组还是链表呢?

我们假设循环队列的长度k(当然实现好之后k传几构造出来长度就是几)为4,我们来分析一下。

首先我们来分析一下用链表行不行

链表结构分析

那链表的话我们是不是正好可以用一个循环链表啊,因为我们现在要实现循环队列嘛:

搞一个循环单链表,循环队列长度为4,所以开4个结点。
在这里插入图片描述
看起来好像还挺合适的。
那现在结点上来直接就开好了,如何判空或者判满呢?
🆗,那我们可以定义两个指针front和rear,来标识队头和队尾的位置
在这里插入图片描述
front和rear都指向第一个结点(front==rear),就可以表示空
那插入数据怎么做呢?
🆗,队尾入数据那对应我们这里的链表来说就是尾插嘛,所以,给rear指向的结点赋要插入的值,然后rear往后走指向下一个结点
在这里插入图片描述
所以rear就是指向最后一个元素的下一个,空队列的时候指向第一个结点。
那我们继续插入
在这里插入图片描述
此时我们发现一个问题,插入满了之后,rear就重新回到了第一个结点,此时front和rear又指向了同一个结点(front==rear
那我们发现判空和判满的条件都是(front==rear
那这样就分辨不开了,怎么办?

那这里解决方式呢不止一种:

比如你可以增加一个size记录有效数据的个数,用size==k来判满。
但是我们这里不采用这种方法,我们还可以这样做:
多开一个结点(开k+1个),就可以解决这个问题
多开一个结点,判空呢还是front==rear,而判满则用rear->next==front
在这里插入图片描述
而且,这个多开的结点也可以存储数据,在后续的操作中,这个空余结点可能是任意一个结点。
我们继续往下看
此时满了,不能再入数据了,那如何删除数据呢——队头出数据
那就是链表的头删,当然这里我们不会真的删除结点,怎么做呢?
在这里插入图片描述
很简单,我们让front往后走就行了(front=front->next),被“删除”的数据也不用抹掉啥的,因为后续再入数据给会他覆盖掉(我这里只是这样画)
那然后再插入呢?
在这里插入图片描述
那我再来pop四次删到空呢?
在这里插入图片描述
删到front==rear就是空了。

那走到这里我们发现这个结构好像就跑通了,用循环单链表实现好像挺棒的。

但是此时我们再来看要实现的几个接口:

在这里插入图片描述
我们发现构造,获取队头元素,插入,删除,判空判满这些都不难搞。
但是获取队尾元素是不是很麻烦啊。因为我们这里是一个单向循环的链表,找尾是比较麻烦的。

当然也可以解决:

可以再增加一个指针prev,记录rear的前一个,这样只要队列不为空,就可以通过prev直接获取队尾元素。
也可以解决。

链表呢我们看了这么久,刚开始感觉还不错,用循环链表刚好有这个环的感觉,非常合适,但是最后发现还是有一些缺点。

那此时呢,我们不妨来考虑一下另外一种结构——用数组实现怎么样呢?

数组结构分析

我们来分析一下,还是以K=4为例:

首先有了上面的分析经验,我们的数组也多开一个空间

在这里插入图片描述
但是数组的话首先看上去就不如上面的链表,因为看着根本不循环。
那如何让它实现循环呢?
那也很简单,走到结尾的时候,我们让它回绕到下标0的位置就行了。

判空判满

那数组实现的话如何判空判满呢?

判空的话很简单
在这里插入图片描述
还是可以以front==rear为空(在哪个位置,就等于该位置下标值)
那判满呢?
rear+1==front吗?
在这里插入图片描述
如果是上面这种情况呢确实是,但是:
如果是下面这样呢?
在这里插入图片描述
rear+1是不是就越界了啊。
那怎么办呢?那就要让它回去,给rear+1模上一个k+1(即数组的长度)
所以统一处理:如果(rear+1)%(k+1)==front,就可以同时处理两种情况的判满
大家可以代入验证一下。
当然也可以给这种情况(rear==k)单独加一个判断,如果此时是满的,front肯定等于0,去判断front是否等于0

入数据出数据

那我们再来分析一下插入删除即队尾入数据和队头出数据:

首先入数据是不是很简单啊
在这里插入图片描述
给rear下标的元素赋值,然后rear++就行了
但是,需要注意:
在这里插入图片描述
如果是这种情况,rear==k再++(等于k+1)是不是就越界了。
那这种情况可以加一个判断if(raer==k+1),让k=0
或者也可以用取模的方式,让它%k+1。当然如果用取模的话就不用判断,因为如果rear<k+1,%k+1之后值是不会变的。

那我们再来看一下队头出数据:

也很简单,正常的pop就直接让front++就行了
在这里插入图片描述
需要注意的也是front走到越界的时候
在这里插入图片描述
此时如果删了5之后,再++就越界了(front==k+1),得让他回到0
那跟上面一样,可以去模k+1,或者加个判断,把它置成0。

取队头队尾元素

那最后再来分析一下取队头和队尾元素:

先来看取队头元素,非常简单:
只要队列不为空(为空题目要求返回-1),直接返回下标front的元素就行了
那取队尾元素呢?
上面分析链表就是取队尾元素麻烦,但是数组,就很简单了:
在这里插入图片描述
下标rear-1的元素不就是队尾元素嘛。
当然,也需要注意一下:
在这里插入图片描述
怕的是这种情况,rear为0,那rear-1是-1,越界了。
但是也很好处理,还是两种方法:
可以单独加一个判断,如果rear==0,把它置成k,其余情况就是rear-1
当然可以写成这样rear==0?k:rear-1
另外一种方式就还是取模可以两种方式统一处理:
(rear-1+k+1)%(k+1) ,此时rear-1是-1嘛,越界了,加个k+1,就变成k了;
而对于其它情况也适用,其它情况rear-1肯定小于k+1,所以模一下不受影响。
简化一下即 (rear+k)%(k+1)

那综合分析一下,其实还是数组会简单一点,所以下面我们就用数组来实现。

3. 代码实现(数组结构)

画图理清思路,写代码还是很简单的:

C语言版本

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

//front==rear就是空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    assert(obj);
    return obj->front==obj->rear;
}

//(rear+1)%(k+1))==front就是满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    return ((obj->rear+1)%(obj->k+1))==obj->front;
    //或
    // if(obj->rear==obj->k)
    //     return obj->front==0;
    // return (obj->rear+1)==obj->front;
}

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* q=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //多开一个空间,解决判满的问题
    q->arr=(int*)malloc(sizeof(int)*(k+1));
    q->front=q->rear=0;
    //开了k+1个空间,但队列实际容量为k
    q->k=k;
    return q;
}

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

    //注意rear++越界的处理
    // if(obj->rear==obj->k+1)
    //     obj->rear=0;
    obj->rear%=(obj->k+1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
        return false;
    obj->front++;

    //注意front++越界的处理
    if(obj->front==(obj->k+1))
        obj->front=0;
    //obj->front%=(obj->k+1);
    return true;
}

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

int myCircularQueueRear(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
        return -1;
    
    //注意rear为0情况的理
    int rear=(obj->rear==0?obj->k:obj->rear-1);
    return obj->arr[rear];
    //return obj->arr[(obj->rear+obj->k)%(obj->k+1)];
}

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

C++版本

class MyCircularQueue {
private:
    vector<int> q;
    int front;
    int rear;
    int _k;
public:
    MyCircularQueue(int k) {
        q.reserve(k+1);
        front=rear=0;
        _k=k;
    }
    
    bool enQueue(int value) {
        if(!isFull())
        {
            q[rear++]=value;
            rear%=(_k+1);
            return true;
        }
        return false;
    }
    
    bool deQueue() {
        if(!isEmpty())
        {
            front++;
            front%=(_k+1);
            return true;
        }
        return false;
    }
    
    int Front() {
        if(!isEmpty())
            return q[front];
        return -1;
    }
    
    int Rear() {
        if(!isEmpty())
        {
            return q[(rear+_k)%(_k+1)];
        }
        return -1;
    }
    
    bool isEmpty() {
        return front==rear;
    }
    
    bool isFull() {
        return (rear+1)%(_k+1)==front;
    }
};

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

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

相关文章

应用层协议 -- HTTPS 协议

目录 一、了解 HTTPS 协议 1、升级版的 HTTP 协议 2、理解“加密” 二、对称加密 1、理解对称加密 2、对称加密存在的问题 三、非对称加密 1、理解非对称加密 2、中间人攻击 3、CA 证书和数字签名 四、总结 一、了解 HTTPS 协议 1、升级版的 HTTP 协议 HTTPS 也是…

prompt提示词:AI英语词典,让AI教你学英语,通过AI实现一个网易有道英语词典

目录 英语词典提问技巧效果图&#xff1a;提示词&#xff1a; 英语词典提问技巧 随着AI工具的出现&#xff0c;学英语也可以变得很简单&#xff0c;大家可以直接通过AI 来帮助自己&#xff0c;提高记忆单词的效率&#xff0c;都可以不需要网易有道词典了&#xff0c;今天我教大…

Grid 布局

文章目录 容器属性display 属性grid-template-columns 和 grid-template-rows 属性row-gap、column-gap、gap 属性grid-template-areas 属性grid-auto-flow 属性justify-items、align-items、place-items 属性justify-content、align-content、place-content 属性grid-auto-col…

AI图书推荐:AI驱动的图书写作工作流—从想法构思到变现

《AI驱动的图书写作工作流—从想法到变现》&#xff08;AI-Driven Book Creation: From Concept to Cash&#xff09;是Martynas Zaloga倾力打造的一本实用指南&#xff0c;它巧妙地将写作艺术与人工智能前沿技术相结合。此书不仅揭示了AI在图书出版领域的无限潜力&#xff0c;…

Delphi 的Show和ShowModal

Show没有返回值是一个过程&#xff0c;焦点可以不在当前窗体&#xff1b; 用法新建一个子窗体&#xff1a; 主窗体&#xff1a; 调用&#xff0c;引用子窗体的单元 调用 showmodal是一个函数有返回值&#xff0c;窗体的处理结果&#xff0c;且只能聚焦到当前窗体 效果都能展示…

echarts实现云台控制按钮效果,方向按钮

效果图 代码 option {color: [#bfbfbf],tooltip: {show: false},series: [{name: ,type: pie,radius: [40%, 70%],avoidLabelOverlap: true,itemStyle: {// borderRadius: 10,borderColor: #fff,borderWidth: 2},label: {show: true,position: inside,fontSize: 36,color: #f…

CST初级教程 二

本教程将讲解CST Studio的视窗操控的基本操作. 3D视窗的快捷操作 动态放大与缩小&#xff08;Dynamic Zoom&#xff09; 将鼠标指针移动到CST Studio图形视窗中&#xff0c;向上滚动鼠标滚轮&#xff0c;可动太放大图形视窗中的显示内容&#xff0c;向下滚动鼠标滚轮即可动态缩…

如何添加所有未跟踪文件到暂存区?

文章目录 如何将所有未跟踪文件添加到Git暂存区&#xff1f;步骤与示例代码1. 打开命令行或终端2. 列出所有未跟踪的文件3. 添加所有未跟踪文件到暂存区4. 验证暂存区状态 如何将所有未跟踪文件添加到Git暂存区&#xff1f; 在版本控制系统Git中&#xff0c;当我们首次创建新文…

《数据结构与算法之美》读书笔记4(递归)

递归是一种应用非常广泛的算法。之后要讲的很多数据结构和算法的编码实现都要用到递归&#xff1a;DFS深度优先搜索&#xff0c;前中后序二叉树遍历等。 推荐注册返佣金这个功能&#xff0c;用户A推荐用户B来注册&#xff0c;用户B推荐用户C来注册。可以说用户B的“最终推荐人…

乐鑫科技收购创新硬件公司 M5Stack 控股权

乐鑫科技 (688018.SH) 宣布收购 M5Stack&#xff08;明栈信息科技&#xff09;的控股权。这一战略举措对于物联网和嵌入式系统领域的两家公司来说都是一个重要的里程碑&#xff0c;也契合了乐鑫和 M5Stack 共同推动 AIoT 技术民主化的愿景。 M5Stack 以其创新的硬件开发方式而闻…

DSP技术及应用——学习笔记一(量化效应)

文章图片内容主要来着老师的PPT&#xff0c;内容为自己总结梳理的学习笔记 二进制定点表示与量化误差 二进制定点表示 基础知识 二进制小数的定点表示 正数小数的定点表示&#xff1a; 思考题&#xff1a;推算字长为16的二进制最大正数与二进制正数 补码&#xff1a;正数不变&…

微电子封装分类及引线键合

1微电子封装分类 - 按功能 模拟电路、存储器传感器、功率电路、光电器件、逻辑电路、射频电路、MEMS、LED等等 - 按结构 分立器件/单芯片封装、多芯片封装、三维封装、真空封装、非真空封装、CSP,BGA/FBGA等等 - 按工艺 线焊封装(WB)、倒装焊封装(FC)、晶圆级封装(WLP)等等 -…

华中农业大学第十三届程序设计竞赛 个人题解(待补)

前言&#xff1a; 注意本篇博客的题解目前并不完整&#xff0c;未来会慢慢补齐的。 进入实验室后接触算法比赛的机会更多了&#xff0c;我接触的题也不再是简单的c语言题了&#xff0c;开始遇到更多我没接触过的算法和难题了&#xff0c;死磕这些难题对现在的我不但花时间而且成…

kubebuilder(4)部署测试

将crd部署到k8s make install 日志&#xff1a; kustomize build config/crd | kubectl apply -f - customresourcedefinition.apiextensions.k8s.io/demoes.tutorial.demo.com created 查看下[rootpaas-m-k8s-master-1 demo-operator]# kubectl api-resources | grep demo de…

python爬虫学习-------scrapy的第一部分(二十九天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

【stomp实战】搭建一套websocket推送平台

前面几个小节我们已经学习了stomp协议&#xff0c;了解了stomp客户端的用法&#xff0c;并且搭建了一个小的后台demo&#xff0c;前端页面通过html页面与后端服务建立WebSocket连接。发送消息给后端服务。后端服务将消息内容原样送回。通过这个demo我们学习了前端stomp客户端的…

BBEdit for Mac v15.0.3激活版 支持多种类型的代码编辑器

BBEdit包含了很多一流的功能&#xff0c;包括GREP图样匹配&#xff0c;搜索和替换多个文件&#xff08;即使未开启的远程服务器上的文件&#xff09;&#xff0c;项目定义的工具&#xff0c;功能导航和众多的源代码语言的语法着色&#xff0c;代码折叠&#xff0c;FTP和SFTP打开…

视频质量度量VQM算法详细介绍

视频质量评价 视频质量评价(Video Quality Assessment,VQA)是指通过主观、客观的方式对视频图像的内容、画质等,进行感知、衡量与评价。 ITU definations subjective assessment: the determination of the quality or impairment of programme-like pictures presented…

后端程序员利用 AI 给网站制作专业 favicon

看看你的 Chrome 浏览器顶部的标签页&#xff0c;每个标签页前面有一个小小的图标&#xff0c;这个就是 favicon&#xff0c;如果你将网页保存到收藏夹&#xff0c;前面也会是这个小图标。这个图标有时候就是网站的 Logo&#xff0c;有时候也不太一样。 上面截图中&#xff0c…

C++学习随笔(10)——string

本章我们来了解一下string类。 目录 1. string类是什么&#xff1f; 1.1 C语言中的字符串 1.2 string类本质 2. 标准库中的string类 2.1 string类 2.2 string类的常用接口说明 1. string类对象的常见构造 2. string类对象的容量操作 3. string类对象的访问及遍历操作…