【数据结构】循环队列

news2024/9/23 1:37:27

🚀 作者简介:一名在后端领域学习,并渴望能够学有所成的追梦人。
🐌 个人主页:蜗牛牛啊
🔥 系列专栏:🛹数据结构、🛴C++
📕 学习格言:博观而约取,厚积而薄发
🌹 欢迎进来的小伙伴,如果小伙伴们在学习的过程中,发现有需要纠正的地方,烦请指正,希望能够与诸君一同成长! 🌹


文章目录

  • 循环队列
  • 设计循环队列

循环队列

队列又称为"先进先出(FIFO)"线性表:插入操作只能在队尾进行,删除操作只能在队首进行。

而循环队列是队列的一种特殊形式,循环队列(也被称为环形队列)是一种线性数据结构,其操作表现基于先进先出原则,并且队尾被连接在队首之后以形成一个循环。

image-20230707175636738

循环队列固定大小,空间能够重复利用。

循环队列可以用数组或者链表实现,那下面我们来分析一下哪种方式更好:

当我们想使用链表实现时,假设我们想要开辟的队列长度为k = 4;front表示队头指针,rear表示队尾指针:

image-20230707201056274

由上图可知,当rear和front 都指向同一个位置时队列为空。

插入一个数据之后:

image-20230707201249071

rear向后移动,此时我们知道rear指向的位置是最后一个数据的下一个位置。

那当我们一直插入数据,直至队列为满:

image-20230707201510530

但是当队列满的时候,我们发现rear 和 front也指向同一个位置。那当队列为空和队列为满的时候,rear和front都指向同一个位置,我们怎么判断此时队列是满还是空?

通过判断front和rear是否为空可以吗?不行,front和rear刚开始都会指向节点,不会为空。

解决方法:1.增加一个size或者flag解决;2.增加一个空余节点。

我们可以多开一个节点,不是哨兵位节点,和头节点一样,此时:

队列为空时:

image-20230707202714806

队列满时:

image-20230707202738386

满的时候不能再插入数据,现在假设我们删除两个数据:

image-20230707202923546

再插入两个数据:

image-20230707203028760

如果我们不断pop,当rear和front指向同一个位置时,队列就变为空了。

那我们可以用单链表来实现循环队列吗?使用链表最不好解决的一个问题就是取队尾数据,单链表不容易取当前节点的前一个结点数据,我们可以通过增加一个指针rear->prev指向前一个结点。

当我们想使用数组实现时,假设我们想要开辟的队列长度为k = 4;front表示队头的下标,rear表示队尾的下标:

使用数组实现同样也面临着front和rear相等时无法判断队列是空还是满的问题,所以我们也可以通过增加一个size或者多开辟一个空间来解决该问题。

image-20230707203649073

当我们插入一个数据后发现rear指向队尾数据的下一个位置,在链表中存在的找队尾问题,在这里找队尾数据变得简单。

image-20230707203858905

当队列为空时下标front == rear:

image-20230707204007467

当队列满时可以通过公式(rear + 1) % (k + 1) == front来判断队列是否满了:

image-20230707204832811

插入数据在rear++之后可以通过rear % (k + 1)来计算插入数据之后rear的下标:

image-20230707205437829

删除数据可以在front++之后通过front % (k + 1)得出删除数据之后front的下标:

image-20230707205816847

练习:现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为 ?

A (rear - front + N) % N + 1
B (rear - front + N) % N
C (rear - front) % (N + 1)
D (rear - front + N) % (N - 1)

假设队列如下图所示:

image-20230707212057185

队内有效长度为2,两个指针相减得到的是两个指针之间的元素个数(rear - front) = 2;上面的四个选项中代入计算只有B符合要求。

设计循环队列

题目:设计循环队列

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

示例:

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1);  // 返回 true
circularQueue.enQueue(2);  // 返回 true
circularQueue.enQueue(3);  // 返回 true
circularQueue.enQueue(4);  // 返回 false,队列已满
circularQueue.Rear();  // 返回 3
circularQueue.isFull();  // 返回 true
circularQueue.deQueue();  // 返回 true
circularQueue.enQueue(4);  // 返回 true
circularQueue.Rear();  // 返回 4

提示:

  • 所有的值都在 0 至 1000 的范围内;
  • 操作数将在 1 至 1000 的范围内;
  • 请不要使用内置的队列库。

思路:

可以使用数组的方式实现循环队列,在结构体中定义一个一级指针int* arr、队头下标front、队尾下标rear以及要开辟的队列长度k;然后通过开辟空间及上面关于循环队列的知识完成循环队列的设计。

注:下面提供的代码第一个是通过if来计算插入时rear下标和删除时front下标的,第二个是通过公式计算的。

代码1:

typedef struct {
    int* arr; //用来指向空间的指针
    int front;//队头下标
    int rear;//队尾下标
    int k;//队列长度
} MyCircularQueue;

构造器,设置队列长度为 k
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* r = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//申请一个结构体指针
    int* tmp = (int*)malloc(sizeof(int) * (k + 1));//多开一个空间
    if(tmp == NULL)
    {
        perror("malloc");
        exit(0);
    }
    r->arr = tmp;//给指针赋值
    r->front = 0;//设置队头下标
    r->rear = 0;//设置队尾下标
    r->k = k;//队列长度
    return r;
}

//检查循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if(obj->rear == obj->front)//相等时为空
        return true;
    return false;
}
// 检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if((obj->rear + 1)%( obj->k + 1) == obj->front)
        return true;
    return false;
}
//向循环队列插入一个元素。如果成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))//队列满时不能再插入
        return false;
    obj->arr[obj->rear++] = value;//插入数据
    if(obj->rear > obj->k)//计算rear下标
    {
        obj->rear = 0;
    }
    return true;
}
//从循环队列中删除一个元素。如果成功删除则返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))//为空时不能删除
        return false;
    obj->front++;
    if(obj->front > obj->k)//计算front下标
    {
        obj->front = 0;
    }
    return true;

}
//从队首获取元素。如果队列为空,返回 -1
int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->arr[obj->front];
}
//获取队尾元素。如果队列为空,返回 -1
int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    int n = obj->rear-1;
    if(n < 0)
    {
        n = obj->k;
    }
    return obj->arr[n];
}
//释放空间
void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    free(obj);
}

代码2:

typedef struct {
    int* arr; //用来指向空间的指针
    int front;//队头下标
    int rear;//队尾下标
    int k;//队列长度
} MyCircularQueue;
//构造器,设置队列长度为 k 。
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));//申请一个结构体指针
    obj->arr=(int*)malloc(sizeof(int)*(k+1));//开辟k+1个int空间用来存储数据,要多开辟一个
    obj->k=k;//队列长度
    obj->front = obj->rear = 0;
    return obj;
}
//检查循环队列是否为空。
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    assert(obj);
    return obj->front == obj->rear; //相等时为空
}
// 检查循环队列是否已满。
bool myCircularQueueIsFull(MyCircularQueue* obj) {
    assert(obj);
    return ((obj->rear+1)%(obj->k+1) == obj->front);
}

//向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    assert(obj);
    if(myCircularQueueIsFull(obj))//队列满时不能再插入
        return false;
    obj->arr[obj->rear++] = value;//插入数据
    obj->rear = obj->rear%(obj->k+1);//计算rear下标
    return true;
}
//从循环队列中删除一个元素。如果成功删除则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(obj))//为空时不能删除
        return false;
    obj->front++;
    obj->front = obj->front % (obj->k+1);//计算front下标
    return true;
}
//从队首获取元素。如果队列为空,返回 -1 。
int myCircularQueueFront(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->arr[obj->front];
}
//获取队尾元素。如果队列为空,返回 -1 。
int myCircularQueueRear(MyCircularQueue* obj) {
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->arr[(obj->rear+obj->k)%(obj->k+1)];
} 

//释放空间
void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    free(obj);
}

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

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

相关文章

OpenGl纹理贴图

给图形赋予颜色时&#xff0c;采用纹理贴图的方式。 每个顶点关联一个纹理坐标(Texture Coordinate),然后在图形的其他片段上进行片段插值(Fragment Interpolation) 顶点坐标如下&#xff1a; float vertices[] { // positions // colors // texture coords 0.2f, 0.2f, 0.0f,…

飞控学习笔记-传感器IMU(陀螺仪+加速度计+磁力计)

陀螺仪 陀螺仪校准 加速度计 加速度不仅包含载体运动的加速度&#xff0c;也包括地球的重力加速度&#xff0c;所以也称为比力计 加速度计标定&#xff08;校准&#xff09; 姿态解算和惯性导航的重要传感器为加速度计、陀螺仪和磁强计&#xff0c;这三种传感器中&#xff0c…

爆肝4万字❤️零基础掌握Verilog HDL

文章目录 0.前言1.Verilog HDL简介1.1 什么是Verilog HDL1.2 verilog发展历史ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ1.3 为什么要使用verilog1.4 IP core 2.语法入门详解2.1 数据类型及常量变量2.1.1 数据类型2.1.2 常量2.1.3 变量2.1.3.1 连线型-wire(assign的使用)2.…

【数据分析 - 基础入门之NumPy④】NumPy基本操作 - 一

知识目录 前言一、NumPy介绍1.1 导入NumPy库1.2 查看NumPy版本1.3 读取图片返回NumPy数组 二、创建NumPy数组的十二种方式2.1 array 和 full2.2 zeros 和 ones2.3 随机数数组2.4 服从正态分布的数组2.5 arrange 和 eye、linspace 三、ndarray属性四、ndarray基本操作4.1 索引4.…

渗透专题丨Vulnhub-Tr0ll靶机打靶

首先安装好靶机&#xff0c;再打开Kali&#xff0c;使用ifconfig查看Kali IP信息&#xff0c;之后使用nmap进行扫描网段&#xff1a; image-20230629200225568 经过排除主机IP和Kali IP&#xff0c;选择192.168.38.139&#xff0c;进行端口扫描&#xff1a; image-20230629201…

PDF怎么转Excel?这三个方法非常实用!

在现代生活中&#xff0c;PDF&#xff08;Portable Document Format&#xff09;已成为广泛使用的文件格式之一。然而&#xff0c;有时我们需要将PDF文件中的数据提取并转换为Excel&#xff08;电子表格&#xff09;格式&#xff0c;以便更方便地编辑、分析和处理数据。本文将介…

剑指offer34.二叉树中和为某一值的路径

用深度遍历的方法找出每一条路径&#xff0c;看看和是否是目标值。 class Solution {Deque<Integer> queue new LinkedList<Integer>();List<List<Integer>> res new LinkedList<List<Integer>>();public List<List<Integer>&…

Component name ‘Channel‘ should always be multi-word

新建vue文件后&#xff0c;会出现第一行代码爆红现象 报错原因: 在组件命名的时候未按照 ESLint 的官方代码规范进行命名&#xff0c;根据 ESLint 官方代码风格指南&#xff0c;除了根组件&#xff08;App.vue&#xff09;以外&#xff0c;其他自定义组件命名要使用大驼峰命名方…

IO流总结:

常用方法&#xff1a; 输入&#xff1a; new bufferedreader(new FileReader(“a.txt”))

Hadoop知识点

1.HDFS写数据流程 客户端通过Distributed FileSystem模块向NameNode请求上传文件&#xff0c;NameNode检查目标文件是否已存在&#xff0c;父目录是否存在。NameNode返回是否可以上传。客户端请求第一个 Block上传到哪几个DataNode服务器上。NameNode返回3个DataNode节点&#…

进程池线程池实现TCP高性能并发通信

进程池线程池实现TCP高性能并发通信 使用进程池与线程池实现并发服务&#xff0c;为多个客户进行接收和发送消息的服务 代码实现 # 导入进程池 from multiprocessing import Pool, cpu_count # 导入线程池 from multiprocessing.pool import ThreadPool from socket import …

动态规划P1

动态规划P1 1.数字三角形模型 1.1只取一条最大路或最小路 1015. 摘花生 只能向下或向右走&#xff0c;求最大能取多少花生 记录每个点最大能取到多少花生&#xff0c;从左上一直推到右下角 d p [ i ] [ j ] m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) a [ …

mybatis-plus记录

1. 导入依赖 注意&#xff1a;springboot和mybatisplus的版本必须一致 导入build工具 <build><plugins><!-- 具体插件&#xff0c;逆向工程的操作是以构建过程中插件形式出现的 --><plugin><groupId>org.mybatis.generator</groupId>&l…

微信视频号配合OBS拉流推流开直播

背景 公司有要求&#xff0c;要实现外部视频流的同步直播&#xff0c;所以特意研究了一下OBS软件结合微信视频号直播的操作步骤。 一、OBS软件配置 &#xff08;一&#xff09;配置拉流 启动OBS软件-点击“”-选择增加“媒体源” 创建源名称 取消本地文件-填写拉流地址-…

MFC List Control控件添加单元格编辑和单元格下拉列表

文章目录 初始化编译栏的创建与销毁下拉列表的创建与销毁自定义编译栏与下拉框的焦点问题点击CListCtrl 事件处理程序双击添加全部代码 ui 设置 初始化 #define IDC_EDIT_CREATEID 3000 #define IDC_COMBOX_CREATEID 3001int e_Item0; //刚编辑的行 int e_SubItem2; //刚…

导入数据解析

Before未定义异常情况 继承 AnalysisEventListener 重写 onException Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvert…

Django的数据库模型迁移命令makemigrations和migrate是否会导致数据库中的数据丢失?

我们知道&#xff0c;如果在Django的文件models.py中写好了数据库模型&#xff0c;要生成对应的数据库&#xff0c;需要执行下面两条命令&#xff1a; python manage.py makemigrations python manage.py migrate其中命令 makemigrations 是生成迁移执行文件&#xff0c;命令 …

tortoiseGit fatal: Could not read from remote repository.

1、问题场景&#xff1a; 使用tortoiseGit工具pull代码的时候报错 git.exe pull --progress -v --no-rebase "origin" fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 无法从存…

投票管理平台达到投票平台投票平台平台投票

现在来说&#xff0c;公司、企业、学校更多的想借助短视频推广自己。 通过微信投票小程序&#xff0c;网友们就可以通过手机拍视频上传视频参加活动&#xff0c;而短视频微信投票评选活动既可以给用户发挥的空间激发参与的热情&#xff0c;又可以让商家和企业实现推广的目的&am…

基于图的路径规划算法对比

基于图的路径规划算法对比 算法说明与实现效果构造路网路网数据转化为图matlab代码 算法说明与实现效果 通过构造邻接矩阵&#xff0c;在构造的路网中找到一个节点通向另一个节点的最短路径&#xff0c;包括DJ&#xff0c;Floyd&#xff0c;A*&#xff0c;ACO&#xff0c;GA算…