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

news2025/1/12 18:08:45

文章目录

  • 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;
    }
};

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

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

相关文章

VBA隐藏技术stomping

1.简介 之前我们介绍了VBA脚本文件的重定向&#xff0c;修改文件中的加载结构并将脚本的二进制文件进行伪装&#xff0c;达到宏代码隐藏的目的&#xff0c;细节请参考上一篇文章"VBA脚本重定向"。该技术具有一定的局限性&#xff0c;只使用脚本重定向技术无法绕过Mic…

http是什么?http的基础知识教程详解(2024-04-24)

1、http的概念 HTTP&#xff08;超文本传输协议&#xff0c;HyperText Transfer Protocol&#xff09;是一种用于分布式、协作式、超媒体信息系统的应用层协议。 HTTP 是万维网&#xff08;WWW&#xff09;的数据通信的基础&#xff0c;设计目的是确保客户端与服务器之间的通…

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

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

Linux实现文件共享

#nfs-utils、rpcbind 软件包来提供 NFS 共享服务 #客户端创建共享文件夹&#xff1a; nmcli c reload nmcli c up ens160 systemctl stop firewalld systemctl disable firewalld rpm -q nfs-utils rpcbind #查看是否安装 systemctl enable rpcbind systemctl enable nfs…

(八)小案例银行家应用程序-排序-数组排序

排序一直有很多的算法&#xff0c;今天我们仅仅来说JavaScript内置的排序方法 ● 字符串 const owners [Jonas, Zach, Adam, Martha]; console.log(owners.sort()); console.log(owners);但是注意&#xff0c;这个方法会改变原有的数组&#xff1b; ● 我们在试试数字 cons…

港股“AIGC第一股”出门问问,凭借什么产品做到上市?

随着人工智能技术的飞速发展&#xff0c;AIGC&#xff08;人工智能生成内容&#xff09;领域逐渐成为资本市场的新宠。在这样的背景下&#xff0c;出门问问&#xff08;股票代码&#xff1a;2438.HK&#xff09;作为AIGC领域的先行者&#xff0c;于2024年4月24日正式登陆港交所…

服务器数据恢复—StorNext文件系统下raid5阵列数据恢复案例

服务器数据恢复环境&#xff1a; 昆腾某型号存储&#xff0c;8个存放数据的存储柜1个存放元数据的存储柜。 元数据存储&#xff1a;8组RAID1阵列1组RAID10阵列4个全局热备硬盘。 数据存储&#xff1a;32组RAID5阵列&#xff0c;划分2个存储系统。 服务器故障&#xff1a; 数据…

matplotlib从起点出发(15)_Tutorial_15_blitting

0 位图传输技术与快速渲染 Blitting&#xff0c;即位图传输、块传输技术是栅格图形化中的标准技术。在Matplotlib的上下文中&#xff0c;该技术可用于&#xff08;大幅度&#xff09;提高交互式图形的性能。例如&#xff0c;动画和小部件模块在内部使用位图传输。在这里&#…

Element-plus DatePicker 日期选择器【正则校验时间范围】

效果图&#xff1a; 利用element-plus中的form表单验证完成效果。 <el-form-item label"检查计划截止日期&#xff1a;" prop"deadline"><el-date-pickerv-model"form.deadline"value-format"YYYY-MM-DD"style"width: …

计算机网络基础认识

本篇文章是我在B站上看到关于计算机网络的介绍视频收到的启发。本篇文章的内容来自【网络】半小时看懂<计算机网络>_哔哩哔哩_bilibili 一、物理层 从常理来说&#xff0c;进行连个设备之间的通讯&#xff0c;首先最容易想到的就是使用一根线连接两个设备进行通讯。但是…

基于单片机的空气质量检测系统设计

摘要:随着社会经济的不断发展,人们的生活水平日益提高,健康与养生成为了全民关注的热点话题,空气质量地不断下降也引起了社会的广泛关注,如何了解家居内空气质量的情况也成了亟需解决的问题。在此背景下,本文针对室内空气的质量问题设计了基于单片机的空气质量检测系统,…

js基础知识(2)

一、事件的含义 JavaScript事件是指在文档或者浏览器中发生的一些特定交互瞬间&#xff0c;比如打开某一个网页&#xff0c;浏览器加载完成后会触发load事件&#xff0c;当鼠标悬浮于某一个元素上时会触发hover事件&#xff0c;当鼠标点击某一个元素时会触发click事件等等。 三…

Excel 公式的定义、语法和应用(LOOKUP 函数、HLOOKUP 函数、VLOOKUP 函数;MODE.MULT 函数; ROUND 函数)

一、公式的定义和语法 二、公式的应用 附录 查找Excel公式使用方法的官方工具【强烈推荐&#xff01;&#xff01;&#xff01;】&#xff1a;Excel 函数&#xff08;按字母顺序&#xff09;【微软官网】 excel 函数说明语法LOOKUP 函数在向量或数组中查找值LOOKUP(lookup_va…

【AI相关】《这就是ChatGPT》读书笔记

《这就是ChatGPT》 斯蒂芬沃尔弗拉姆 这本书用了两天就一口气读完了&#xff0c;通篇读完后&#xff0c;这本书主要是介绍了ChatGPT怎么能做到生成内容的一些背后的原理逻辑&#xff0c;总结一下这本书是ChatGPT通过大量的数据&#xff08;这些数据来自网络、书籍等等类似于数据…

09.JAVAEE之网络初识

1.网络 单机时代 >局域网时代 >广域网时代 >移动互联网时代 1.1 局域网LAN 局域网&#xff0c;即 Local Area Network&#xff0c;简称LAN。 Local 即标识了局域网是本地&#xff0c;局部组建的一种私有网络。 局域网内的主机之间能方便的进行网络通信&#xff0…

Activiti——将绘制的流程图存入act数据库并进行流程推进与状态流转

文章目录 前言流程图入库操作 RepositoryService项目结构数据库连接配置文件入库Java测试代码zip 方式进行流程的批量部署 流程启动 RuntimeService待处理任务查看 TaskService流程状态的扭转查询流程定义信息 RepositoryService查询正在执行的流程实例 RuntimeService已部署流…

git简介及安装

Git | Git简介与安装 文章目录 Git | Git简介与安装一、Git简介二、Git安装Linux-centosLinux-ubuntu 一、Git简介 存在需求&#xff1a;对于一个文档&#xff0c;由于编写思路或者当前文档丢失&#xff0c;可能存在想要历史版本的需求&#xff0c;并且需要知道每个版本都修改了…

MySQL主从的应用

说明&#xff1a;本文介绍MySQL主从在实际中的应用。主从搭建和问题参考下面两篇文章&#xff1a; MySQL主从结构搭建 搭建MySQL主从结构时的问题 数据迁移 当我们搭建完MySQL主从&#xff0c;第一步当然是把历史数据导入到主从结构中。有以下两种方式&#xff1a; 开启主从…

Mysql个人总结

前言 又来水字数啦&#xff0c;这次主要讲一下MySQL的常用概念&#xff0c;难点的就必须上项目讲解了&#xff0c;而且比较基础面试基本都会问一些&#xff0c;用的话&#xff0c;不少优化都从这里入手 操作数据库 1、创建数据库 CREATE DATABASE [IF NOT EXISTS] 数据库名;…

如何设置微信自动回复?教你快速上手!

自动回复对于需要在微信上洽谈业务的人来说&#xff0c;无疑是非常实用的一个功能。 下面就一起来看看微信管理系统的机器人自动回复都有哪些设置吧&#xff01; 1、自动通过好友 只要有新的好友请求发送到你的微信账号&#xff0c;系统会自动通过该请求&#xff0c;无需手动…