你真的了解环形队列吗?(学习数据结构必须掌握的模型)

news2024/9/25 17:13:22

目录

0.前言

1. 什么是环形队列

 2. 如何使用数组结构 / 链表结构 对环形队列封装

3. 代码手撕环形队列各个接口

3.1 代表封装一个环形队列

3.2 环形队列的初始化

3.3 环形队列的插入

3.4环形队列的删除

3.5环形队列的判空

3.6环形队列的判满

3.7环形队列的队头

3.8环形队列的队尾

3.9环形队列的销毁


0.前言

4栈和队列OJ题集合/CircularQueue.h · onlookerzy123456qwq/data_structure_practice_primer - 码云 - 开源中国 (gitee.com)icon-default.png?t=N176https://gitee.com/onlookerzy123456qwq/data_structure_practice_primer/blob/master/4%E6%A0%88%E5%92%8C%E9%98%9F%E5%88%97OJ%E9%A2%98%E9%9B%86%E5%90%88/CircularQueue.h本文所有代码资源已经上传至gitee,如上可自取。

622. 设计循环队列 - 力扣(LeetCode)icon-default.png?t=N176https://leetcode.cn/problems/design-circular-queue/这是本题的OJ链接,是骡子是马,可以拉出去练练。

1. 什么是环形队列

环形队列,也称循环队列,它是一种特殊的队列,则其必首先符合队列的性质,即先进先出,后进后出(First In First Out)。 

环形队列特殊在哪里呢?

1.这个队列是定长的,在初始化的时候,这个队列的长度就已经定了,即再也不能改变它所能容纳元素的最大数量了。

2.这个队列从抽象图看来,形状上不是平常我们看到是直线型的,而是一个环形的

3.这个队列的队头和队尾(首和尾),在插入删除的过程当中,是一直在不断变化的。相比普通队列,它的首尾并不是在完全固定的位置

 环形队列的逻辑是:一个head记录当前环形队列的队头位置,一个tail记录当前环形队列的队尾位置(当然我们这里tail实际代表意义是队尾元素的下一个位置),如果删除的话就前移head代表删除,如果要插入的话,就后移tail,代表增加一个环形队列的元素。这是环形队列的插入删除逻辑。

那环形队列毕竟是定长的队列,当超过定长,即把这个环形队列插满时,那就会插入失败。所以我们需要设定一个head和tail的状态,以之作为判断环形队列是否为满的标准。同时我们也要设定一个head和tail的状态,以之作为判断环形队列是否为空的标准

我们这样设定:初始状态的环形队列,即环形队列是空的话,此时head和tail是重合的。

然后你插入一个元素,队列是从队尾插入的,所以我们就把当前tail所指向的位置插入新元素,然后++tail,指向队尾的下一个位置(因为如果不++tail的话,head和tail还是重合的,这是时候,我们是认为此环形队列是空)。

如果再一直一直插入新元素,直到插入为满的时候,(tail能最远到达的位置决定了最多能插入的元素,而且铭记tail指向的是队尾元素的下一个位置head和tail重合代表环形队列为空),根据上述规则的限定,我们最终可以允许tail最远到head的前一个位置。

这样虽然会浪费掉一个小元素的空间,但是这就一小点并不重要,实在不行,你想最多插入k个有效元素,你再多开一个元素的空间(k+1)不就中了嘛~

当然啦,删除元素,队列就是删除队头的元素,然后head指向的就是队头元素,我们++head即可完成删除。

 

 2. 如何使用数组结构 / 链表结构 对环形队列封装

使用链表结构进行对一个环形队列的实现,我们很简单,就是用head和tail两个指针,然后记录一个当前长度int num,然后就依次进行对于该队列的插入删除,这个和我们这篇博客所写的j基本一样:3.用C语言实现队列

然后我们再需要做的就是,当总数据num达到了定长,即达最大可容纳数目之后,那我们就禁止插入,这个很简单的。

本博客,我们使用数组结构仿生实现该环形队列,你可能会产生疑问:链表姑且可以通过next成员指针找到下一个元素做到首尾相接,那一个数组的首尾又不相连,怎么会实现出一个环形队列呢?其实,我们是借助 % array_len 的方式,实现的一手首尾互通可以循环的走。

举一个简单的例子:

 我们在插入的时候,是直接在tail位置进行插入(因为tail始终是指向最后一个队尾元素的下一个元素),但是上图当中,我们刚刚插入7这个元素之后,就需要我们++tail,但是这样就完了吗?这样会产生越界,而且你是环形队列,你index==8的下一个位置应该是循环回头[0]!!!我们的解决方法是%arr_len的方式。(8 + 1)之后再 % 数组长度9,结果是0,就回到了数组头的位置了!!!

3. 代码手撕环形队列各个接口

3.1 代表封装一个环形队列

我们选择了数组结构实现环形队列,那么我们就要在堆区开辟一个数组,所以我们存储封装一个数组指针成员int* _a。

然后任何一个环形队列都是定长的,所以我们不妨定义一个int _k代表当前队列最多能存储的有效元素的数量。(这个_k也是)

然后环形队列控制首尾也是必要的,所以我们定义一个head和tail分别管理队头和队尾,即分别负责删除和插入(tail指代的是队尾元素的下一个位置,空时与head重合)。

//我们使用数组来模拟实现循环队列
typedef struct {
    int* _a;    //数组实体
    int _k;     //最大容纳有效数据个数
    int _head;  //队头[删除]
    int _tail;  //队尾[插入]
} MyCircularQueue;

3.2 环形队列的初始化

我们创建一个环形队列,或者任何一个类对象,就要对之完成初始化,这里我们把创建环形队列的方法封装成一个接口myCircularQueueCreate,返回的是一个在堆区创建环形队列的指针,然后在这个接口当中我们对之完成初始化

MyCircularQueue* myCircularQueueCreate(int k) {
    //在堆区开辟一个环形队列变量MyCircularQueue
    MyCircularQueue* p_circularQueue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    p_circularQueue->_k = k;
    p_circularQueue->_head = 0;
    p_circularQueue->_tail = 0; //默认一开始首尾指针指向[0]的位置
    //构造可以存储k个有效数据的环形队列空间,需要我们开辟k+1的空间
    int* ptmp = (int*)malloc(sizeof(int)*(k+1));
    if(ptmp==NULL){
        exit(1);
    }
    p_circularQueue->_a = ptmp;
    //返回创造的环形队列实体
    return p_circularQueue;
}

3.3 环形队列的插入

这里要两个需要特别注意的点:一个判断当前环形队列是否为满,环形队列是定长的,如果为满就是插入失败。第二个点是,tail的更新需要考虑从尾至首

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //向循环队列插入一个元素。如果成功插入则返回真。
    //循环队列存满了就不让存了,所以存在false插入失败的情况

    //1.判断是否为满(tail->next==head)
    //tail的next不能简单++,我们需要考虑到从数组尾[k]到数组头[0]的情况,所以(tail++);tail%=(k+1);即可
    int next_tail = (obj->_tail+1)%(obj->_k+1);
    if(next_tail==obj->_head)
    {
        return false;
    }
    //2.进行插入
    obj->_a[obj->_tail] = value;
    //更新tail
    obj->_tail = next_tail;
    return true;
}

3.4环形队列的删除

如果为空,不能删除;删除就是直接++head,更新头的位置即可完成(伪删除法),但是此时还时要考虑head从尾至首这样的一个特殊情况。

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //从循环队列中删除一个元素。如果成功删除则返回真。
    //存在是空的环形队列,所以存在删除失败false的情况

    //1.判断是否为空:head与tail重合
    if(myCircularQueueIsEmpty(obj)) //obj->_head==obj->_tail
    {
        return false;
    }
    //2.删除就是把head++,指向下一个元素即可 
    //可是head也存在从数组尾更新到数组头的情况
    obj->_head++;
    obj->_head%=(obj->_k+1);
    return true;
}

3.5环形队列的判空

我们设定head和tail重合的时候为空。

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    //两个指针重合代表空
    return obj->_head==obj->_tail;
}

3.6环形队列的判满

满的时候我们设定的状态是tail的next值就是head,那就是满状态。(当然这里要记住:我们存在从数组尾n-1到头0的转变,所以说不能直接无脑对tail+1哦)

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    //tail->next==head代表满
    return (((obj->_tail)+1)%(obj->_k+1))==obj->_head;
}

3.7环形队列的队头

int myCircularQueueFront(MyCircularQueue* obj) {
    //从队首获取元素。如果队列为空,返回 -1 。
    //存在从空队列中取数据的情况,这种情况是非法情况
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    //队头的数据,就是head指向的数据
    return obj->_a[obj->_head];
}

3.8环形队列的队尾

int myCircularQueueRear(MyCircularQueue* obj) {
    //获取队尾元素。如果队列为空,返回 -1 。
    //存在从空队列中取数据的情况,这种情况是非法情况
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    //tail指向的是下一个要插入的数据的位置
    //所以上一个队尾的数据应该是tail-1的位置
    //但是在这种情况下,tail也有从队首回到队尾的情况出现
    //所以我们需要淡出讨论这种情况
    if(obj->_tail == 0)
    {
        return obj->_a[obj->_k];
    }
    return obj->_a[obj->_tail-1];
}

3.9环形队列的销毁

环形队列的成员变量都是定义在堆区的,然后指向的数组空间也是存储在堆区的,我们使用完环形队列要对之销毁。

void myCircularQueueFree(MyCircularQueue* obj) {
    //释放所有的堆区空间
    free(obj->_a);
    free(obj);
}

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

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

相关文章

NCNN量化详解2

1 NCNN量化算法简介 量化算法介绍的文章的话,下面这篇文章的大佬 @章小龙 介绍的比我好多啦。虽然介绍的是NVIDIA TensorRT的算法,但是NCNN是参考其算法做出来的,方法几乎一样 首先NVIDIA有一个PPT,很好的阐述了他们的方案,推荐大家也去看一下: https://link.zhihu.com…

我来了,RK第七届开发者大会

大家好,我是发哥的朋友windsnake「也是发哥文章里面经常出现的邓总」。今年终于是有时间参加了一次Rk的开发者大会,这篇文章记录下自己的所见所闻,算是对自己福州之行的一次总结。从深圳出发月亮还没睡醒的早上,坐5号线杀到深圳北…

【项目设计】高并发内存池(二)[高并发内存池整体框架设计|threadcache]

🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…

【无标题String、StringBuffer、StringBuilder区别】

一、背景。 这篇文章主要介绍了String、StringBuffer、StringBuilder的区别详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下。放假在家里休息,闲来无事,想…

华为OD机试题,用 Java 解【汽水瓶】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

【华为OD机试模拟题】用 C++ 实现 - 寻找路径 or 数组二叉树(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 获得完美走位(2023.Q1) 文章目录 最近更新的博客使用说明寻找路径 or 数组二叉树题目输入输出描述示例一输入输出示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过…

华为开源自研AI框架昇思MindSpore数据变换:Transforms

目录一、环境准备1.进入ModelArts官网2.使用CodeLab体验Notebook实例二、数据变换 TransformsCommon TransformsComposeVision TransformsRescaleNormalizeHWC2CWHText TransformsBasicTokenizerLookupLambda Transforms通常情况下,直接加载的原始数据并不能直接送入…

线程安全之synchronized和volatile

目录 1.线程不安全的原因 2.synchronized和volatile 2.1 synchronized 2.1.1 synchornized的特性 2.1.2 synchronized使用示例 2.2 volatile 我们先来看一段代码: 分析以上代码,t1和t2这两个线程的任务都是分别将count这个变量自增5000次&#xff…

redis(5)列表List

Redis列表 Redis单键多值:Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。 它的底层实际是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差。 常…

【Linux学习笔记】7.Linux vi/vim

前言 本章介绍Linux的vi/vim。 Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在。 但是目前我们使用比较多的是 vim 编辑器。 vim 具有程序编辑的能力,可以主动的以字体颜色辨别语法的正确性&#xff0c…

【华为OD机试模拟题】用 C++ 实现 - 优秀学员统计(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 货币单位换算(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 选座位(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 停车场最大距离(2023.Q1) 【华为OD机试模拟题】用 C++ 实现 - 重组字符串(2023.Q1) 【华为OD机试模…

HashMap数据结构

HashMap概述 HashMap是基于哈希表的Map接口实现的&#xff0c;它存储的是内容是键值对<key,value>映射。此类不保证映 射的顺序&#xff0c;假定哈希函数将元素适当的分布在各桶之间&#xff0c;可为基本操作(get和put)提供稳定的性能。 HashMap在JDK1.8以前数据结构和存…

网络流与图(三)

经过两篇文章的篇幅&#xff0c;我们介绍了最小费用网络流模型以及解决的算法。今天我们介绍网络流模型的现实应用案例&#xff0c;并针对一些特殊的情景提出更高效的解决算法。传送门&#xff1a;网络流与图&#xff08;一&#xff09;网络流与图&#xff08;二&#xff09;1运…

多模态预训练模型综述

经典预训练模型还未完成后续补上预训练模型在NLP和CV上取得巨大成功&#xff0c;学术届借鉴预训练模型>下游任务finetune>prompt训练>人机指令alignment这套模式&#xff0c;利用多模态数据集训练一个大的多模态预训练模型&#xff08;跨模态信息表示&#xff09;来解…

【数据结构】栈的接口实现(附图解和源码)

栈的接口实现&#xff08;附图解和源码&#xff09; 文章目录栈的接口实现&#xff08;附图解和源码&#xff09;前言一、定义结构体二、接口实现&#xff08;附图解源码&#xff09;1.初始化栈2.销毁栈3.入栈4.判断栈是否为空5.出栈6.获取栈顶元素7.获取栈中元素个数三、源代码…

【华为OD机试模拟题】用 C++ 实现 - 字符匹配(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 获得完美走位(2023.Q1) 文章目录 最近更新的博客使用说明字符匹配题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。…

C++10:非类型模板参数以及模板的特化

目录 非类型模板参数 模板的特化 模板类的特化 1.全特化 2.偏特化 模板其实还有其他的玩法&#xff0c;比如非类型模板参数以及模板的特化。 非类型模板参数 在记述非类型模板参数前&#xff0c;我们认识一下C中一个比较鸡肋的类&#xff0c;array #include<iostream&g…

Kotlin1.8新特性

Kotlin1.8.0新特性 新特性概述 JVM 的新实验性功能&#xff1a;递归复制或删除目录内容提升了 kotlin-reflect 性能新的 -Xdebug 编译器选项&#xff0c;提供更出色的调试体验kotlin-stdlib-jdk7 与 kotlin-stdlib-jdk8 合并为 kotlin-stdlib提升了 Objective-C/Swift 互操作…

MATLAB绘制泰勒图(Taylor diagram)

泰勒图&#xff08;Taylor diagram&#xff09; 泰勒图是Karl E. Taylor于2001年首先提出&#xff0c;主要用来比较几个气象模式模拟的能力&#xff0c;因此该表示方法在气象领域使用最多&#xff0c;但是在其他自然科学领域也有一定的应用。 泰勒图常用于评价模型的精度&…

使用命令别名一键启动arthas

1. 使用命令别名启动arthas 确保单板上有jdk和arthas jdk目录&#xff1a;/home/xinliushijian/arthas/jdk arthas目录&#xff1b;/home/xinliushijian/arthas su xinliushijian编写脚本messi.sh cd /home/xinliushijian/arthas vi messi.sh 内容如下&#xff1a; #!/bin/ba…