【数据结构】如何设计循环队列?图文解析(LeetCode)

LeetCode链接:622. 设计循环队列 - 力扣(LeetCode)

目录

做题思路

只开辟 k 个空间

多开一个空间

代码实现

1. 循环队列的结构

2. 开辟空间

3. 判断空

4. 判断满

5. 队尾插入数据

6. 队头删除数据

7. 获取队头元素

8. 获取队尾元素

9. 销毁队列

全部代码


做题思路

  • 设计循环队列,使用数组或链表都可以,各有优劣
  • 本文使用数组实现
  • 本文使用C语言实现

假设队列长度 k = 4

多开一块空间(开辟 k+1 块空间)可以方便区分空和满

为什么?

举个栗子:

只开辟 k 个空间

如果只开辟 k 个空间(假设 k = 4):

  • front(队头)
  • rear(队尾)
  • front 和 rear 初始都为 0

如果插入一个数据呢?

front 不变,rear 向后移动,如下图:

理想的判断空条件:

  • 队头 == 队尾,队列为空
  • 队头 != 队尾,队列非空

目前看来似乎可以正常判断队列是否为空

那如果队列满了呢?

如上图所示,队列满了

rear 又回到了 front 的位置

现在怎么判断满呢?

rear == front 吗?

似乎和前面判断空冲突了

想判断必须要特殊处理:比如加一个变量 size 来记录队列中元素的个数

这种方法虽然可以,但是会让代码显得很臃肿,于是我选择了另一种方法:

多开一个空间

如果多开了一个空间(开辟 k+1 个空间):

  • 这里多开的空间是不存放数据的
  • 这块空间存在的意义就是为了方便区分空和满

下面我将演示一下如何判断:

判断空:

  • 队头 == 队尾,队列为空
  • 队头 != 队尾,队列非空

判断满:

  • 队尾的下一个 == 队头,队列已满
  • 队尾的下一个 != 队头,队列未满

如下图:

那么好,这里简单介绍了一下我的实现思路:

  • 使用C语言实现
  • 使用数组实现
  • 数组多开一块空间

接下来就是详细的实现方法


代码实现

本题要求:

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

1. 循环队列的结构

//循环队列的结构
typedef struct
{int* a;     //一个数组int front;  //存放队头的下标int rear;   //存放队尾的下标int k;      //队列长度
} MyCircularQueue;

2. 开辟空间

//开辟空间并初始化
MyCircularQueue* myCircularQueueCreate(int k)
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//多开一个方便区分空和满obj->a = (int*)malloc(sizeof(int) * (k + 1));obj->front = obj->rear = 0;obj->k = k;return obj;
}

3. 判断空

前面说过了,队头等于队尾时,队列为空

//检测循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->front == obj->rear;
}

4. 判断满

注意这里有一些坑点

前面说过:队尾+1等于队头时,队列为满

但是我用的是数组,不是链表队尾+1有可能会越界

如上图:rear+1 = 5,越界了

怎么办呢?可以采用取余数(%)的方法,即:(rear+1)%(k+1),让 rear+1 变回 0,达到循环的效果。

情况1:队尾+1越界了

(rear + 1) % (k + 1) = 0

front = 0

(rear + 1) % (k + 1) = front,说明循环队列已满。

情况2:队尾+1没越界

(rear + 1) % (k + 1) = 4

front = 0

(rear + 1) % (k + 1) != front,说明循环队列未满。

//检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear + 1) % (obj->k + 1) == obj->front;
}

5. 队尾插入数据

这里需要一些操作,还是越界的问题,插入数据后队尾可能会越界,如下图:

rear 处插入数据后,rear 需要向后移动,导致越界

1)判断队列是否已满,满了是不能插入的,直接返回 false

2)在队尾插入数据

3)队尾++,可能会越界

4)依旧采用取余数的方法让队列循环起来,即:rear = rear % (k + 1)

//向循环队列插入一个元素。如果成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{if (myCircularQueueIsFull(obj))return false;obj->a[obj->rear] = value;obj->rear++;obj->rear %= (obj->k + 1);return true;
}

6. 队头删除数据

删除数据后,队头也可能会有越界的情况,如下图:

删除 front 处的数据后,front 需要向后移动,导致越界

1)判断队列是否为空,空队列是不能删数据的,直接返回 false

2)删除数据很简单,直接让队头++即可

3)取余数避免越界:front = front % (k + 1)

//从循环队列中删除一个元素。如果成功删除则返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return false;obj->front++;obj->front %= (obj->k + 1);return true;
}

7. 获取队头元素

情况1:队列为空

根据题意,返回 -1

情况2:队列不为空

直接返回数组中下标为 front 的元素即可

//从队首获取元素。如果队列为空,返回-1
int myCircularQueueFront(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return -1;elsereturn obj->a[obj->front];
}

8. 获取队尾元素

情况1:队列为空

根据题意,返回 -1

情况2:队列不为空

  • 需要返回数组中下标 rear 的前一个元素(rear-1
  • 此操作可能会导致越界
  • 用取余数的方式避免越界:(rear + k) % (k + 1)

如上图:这种情况下强行获取 rear 的前一个元素会导致越界

//获取队尾元素。如果队列为空,返回-1
int myCircularQueueRear(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return -1;elsereturn obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}

9. 销毁队列

销毁所有动态开辟的空间

//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj)
{free(obj->a);free(obj);
}

全部代码

//循环队列的结构
typedef struct
{int* a;     //一个数组int front;  //存放队头的下标int rear;   //存放队尾的下标int k;      //队列长度
} MyCircularQueue;//开辟空间并初始化
MyCircularQueue* myCircularQueueCreate(int k)
{MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//多开一个方便区分空和满obj->a = (int*)malloc(sizeof(int) * (k + 1));obj->front = obj->rear = 0;obj->k = k;return obj;
}//检测循环队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{return obj->front == obj->rear;
}//检查循环队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{return (obj->rear + 1) % (obj->k + 1) == obj->front;
}//向循环队列插入一个元素。如果成功插入则返回真
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{if (myCircularQueueIsFull(obj))return false;obj->a[obj->rear] = value;obj->rear++;obj->rear %= (obj->k + 1);return true;
}//从循环队列中删除一个元素。如果成功删除则返回真
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return false;obj->front++;obj->front %= (obj->k + 1);return true;
}//从队首获取元素。如果队列为空,返回-1
int myCircularQueueFront(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return -1;elsereturn obj->a[obj->front];
}//获取队尾元素。如果队列为空,返回-1
int myCircularQueueRear(MyCircularQueue* obj)
{if (myCircularQueueIsEmpty(obj))return -1;elsereturn obj->a[(obj->rear + obj->k) % (obj->k + 1)];
}//销毁循环队列
void myCircularQueueFree(MyCircularQueue* obj)
{free(obj->a);free(obj);
}

本文完

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

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

相关文章

MySql学习笔记11——DBA命令介绍

DBA命令 数据导入 要进入Mysql 创建数据库 create database database_name;使用数据库 use database_name;初始化数据库 source .sql文件地址,不能加双引号;数据导出 要在windows的dos环境下进行 导出数据库 mysqldump database_name > 存放…

包管理工具--》npm的配置及使用(一)

目录 🌟概念 🌟背景 🌟前端包管理器 🌟包的安装 本地安装 全局安装 🌟包配置 配置文件 保存依赖关系 🌟包的使用 🌟写在最后 🌟概念 模块(module&#xff09…

【LeetCode】83. 删除排序链表中的重复元素

83. 删除排序链表中的重复元素(简单) 方法:一次遍历 思路 由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。 从指针 cur 指…

脚本:python实现樱花树

文章目录 代码效果 代码 from turtle import * from random import * from math import * def tree(n, l):pd () # 下笔# 阴影效果t cos ( radians ( heading () 45 ) ) / 8 0.25pencolor ( t, t, t )pensize ( n / 3 )forward ( l ) # 画树枝if n > 0:b random () *…

搭建vue3项目并git管理

搭建vue3项目 采用vue3的create-vue脚手架搭建项目,底层是vite,要求环境 node 16.0及以上(node -v检查node版本) 在文件夹右键->终端-> npm init vuelatest,输入项目名称,根据需要选择是否装包 src…

SpringBoot运维实用篇、打包、运行、高级配置、多环境开发、日志

文章目录 SpringBoot运维实用篇YW-1.SpringBoot程序的打包与运行程序打包程序运行SpringBoot程序打包失败处理命令行启动常见问题及解决方案SpringBoot项目快速启动(Linux版) YW-2.配置高级YW-2-1.临时属性设置YW-2-2.配置文件分类YW-2-3.自定义配置文件…

NSSCTF2nd与羊城杯部分记录

文章目录 前言[NSSCTF 2nd]php签到[NSSCTF 2nd]MyBox[NSSCTF 2nd]MyHurricane[NSSCTF 2nd]MyJs[NSSCTF 2nd]MyAPK羊城杯[2023] D0nt pl4y g4m3!!!羊城杯[2023]ezyaml羊城杯[2023]Serpent羊城杯[2023]EZ_web羊城杯[2023]Ez_misc总结 前言 今天周日,有点无聊没事干&a…

机械零件保养3d模拟演示打消客户购买顾虑

复杂机械的工作运转是复杂的,想要对机械有深度的理解和迭代,必须了解它的运转原理及参数,复杂机械运行原因教学存在着不可视、系统庞杂及知识点多等弊病,3D虚拟展示是基于web3d网页运行的三维页面,可以将复杂机械运行过…

浏览器端vscode docker搭建(附带python环境)

dockerfile from centos:7 #安装python环境 run yum -y install wget openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel zlib-devel libffi-devel gcc make run wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz run tar -xvf Python-3.9.…

【Android Framework系列】第14章 Fragment核心原理(AndroidX版本)

1 简介 Fragment是一个历史悠久的组件,从API 11引入至今,已经成为Android开发中最常用的组件之一。 Fragment表示应用界面中可重复使用的一部分。Fragment定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。…

Java 日志技术

所以说,要学Logback! 配置文件 Logback提供了一个核心配置文件logback.xml,日志框架在记录日志时会读取配置文件中的配置信息,从而记录日志的形式。 可以配置日志输出的位置是文件还是控制台可以配置日志输出的格式还可以配置日…

【数据结构】树的基础入门

文章目录 什么是树树的常见术语树的表示树的应用 什么是树 相信大家刚学数据结构的时候最先接触的就是顺序表,栈,队列等线性结构. 而树则是一种非线性存储结构,存储的是具有“一对多”关系的数据元素的集合 非线性 体现在它是由n个有限结点(可以是零个结点)组成一个具有层次关…