如何设计循环队列(两种方法)

news2024/11/18 13:57:05

文章目录

  • 前言
  • 一、方法一:数组法
  • 二、方法二.链表法
  • 总结

前言

前面有提到过队列的知识,这次来说一下怎么设计一个循环队列


一.循环队列(力扣)

. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。icon-default.png?t=N7T8https://leetcode.cn/problems/design-circular-queue/description/

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

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

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

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

思路:循环队列无论使用数组实现还是链表实现,都要多开一个空间,也就意味着要是存K个数据的循环队列,就要开K+1个空间,不然无法实现判空和判满

方法一:数组法

注意数组法的判空和判满

判空:就是front==tail的时候就是空的,判满:当(tail+1)%(k+1)==front就是满的

1.0初始化

初始化一个数组,有头front,尾tail,数组明a

typedef struct {
	int* a;
	int front;
	int tail;
	int k;

} MyCircularQueue;

1.1创建

MyCircularQueue* myCircularQueueCreate(int k) 
{
	//开辟一个循环队列的空间
	MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	//开辟一个数组的空间用来实现循环队列,要多开辟一个
	q->a = (int*)malloc(sizeof(int) * (k + 1));
	//初始化
	q->front = q->tail = 0;
	q->k = k;
	return q;
}

1.2判空,判满

判空:就是front==tail的时候就是空的,判满:当(tail+1)%(k+1)==front就是满的

//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->front == obj->tail;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
	return (obj->tail + 1) % (obj->k + 1) == obj->front;
}

1.3插入

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	//如果循环队列是满的就不能插入
	if (myCircularQueueIsFull(obj))
		return false;
	//把末尾的值插入值
	obj->a[obj->tail] = value;
	//然后tail的往后走
	++obj->tail;
	//防止数组越界,%(k+1)把下标都控制在k之内
    //把越界的重置
	obj->tail %= (obj->k + 1);
	return true;
}

1.4删除

数组的删除不用向链表这些Pop,直接覆盖就可以了

//数组的删除不用向链表这些Pop,直接覆盖就可以了
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	//如果是空的就不能删
	if (myCircularQueueIsEmpty(obj))
		return false;
	//头往前走
	++obj->front;
	//止数组越界,%(k+1)把下标都控制在k之内
	obj->front %= (obj->k + 1);
	return true;
}

1.5拿出最后一个数

int myCircularQueueRear(MyCircularQueue* obj) {
	//如果是空的就拿不了
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	//存在特殊情况,当tail为0时,尾才最后,所以不能直接拿出tail之前的数
	if (obj->tail == 0)
		return obj->a[obj->k];
	else
		return obj->a[obj->tail - 1];
}

1.6拿出头数据和销毁

//直接拿出
int myCircularQueueFront(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}

	return obj->a[obj->front];

}

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

1.7总代码

注意把判空判满提前引用!!!

typedef struct {
	int* a;
	int front;
	int tail;
	int k;

} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) 
{
	//开辟一个循环队列的空间
	MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	//开辟一个数组的空间用来实现循环队列,要多开辟一个
	q->a = (int*)malloc(sizeof(int) * (k + 1));
	//初始化
	q->front = q->tail = 0;
	q->k = k;
	return q;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	//如果循环队列是满的就不能插入
	if (myCircularQueueIsFull(obj))
		return false;
	//把末尾的值插入值
	obj->a[obj->tail] = value;
	//然后tail的往后走
	++obj->tail;
	//防止数组越界,%(k+1)把下标都控制在k之内
	obj->tail %= (obj->k + 1);
	return true;
}

//数组的删除不用向链表这些Pop,直接覆盖就可以了
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	//如果是空的就不能删
	if (myCircularQueueIsEmpty(obj))
		return false;
	//头往前走
	++obj->front;
	//止数组越界,%(k+1)把下标都控制在k之内
	obj->front %= (obj->k + 1);
	return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}

	return obj->a[obj->front];

}

int myCircularQueueRear(MyCircularQueue* obj) {
	//如果是空的就拿不了
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}
	//存在特殊情况,当tail为0时,尾才最后,所以不能直接拿出tail之前的数
	if (obj->tail == 0)
		return obj->a[obj->k];
	else
		return obj->a[obj->tail - 1];
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->front == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	return (obj->tail + 1) % (obj->k + 1) == obj->front;
}

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

方法二.链表法

2.0初始化

先初始化一个链表,在定义结构

typedef struct listNode {
    int data;
    struct Node* next;
}Node;

typedef struct {
    Node* front;
    Node* tail;
    int k;
}MyCircularQueue;

2.1创建

这个是最难的部分,就是在创建的时候要创造一个循环链表,注意:这里其实已经开辟了k+1个空间了,不懂的自己画图

MyCircularQueue* myCircularQueueCreate(int k) {

    MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    cq->front = cq->tail = (Node*)malloc(sizeof(Node));
    cq->k = k;
    //创造一个循环链表
    //这里其实已经开辟了k+1个空间了注意
    while (k)
    {
        Node* cur= (Node*)malloc(sizeof(Node));
        cur->data = 0;
        cur->next = NULL;
        cq->tail->next =cur;
        cq->tail= cq->tail->next;
        k--;
    }
    //开辟好了之后还要把尾和头发一起
    cq->tail->next =cq->front;
    cq->tail= cq->front;

    return cq;
}

2.2判空,判满

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    return obj->front == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->tail->next == obj->front;
}

2.3插入

//插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //先判满
    if (myCircularQueueIsFull(obj))
        return false;
    //直接在尾上插入
    obj->tail->data = value;
    obj->tail= obj->tail->next;
    return true;
}

2.4删除

//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //先判空
    if (myCircularQueueIsEmpty(obj))
        return false;
    //头删
    obj->front = obj->front->next;
    return true;
}

2.5去头元素

int myCircularQueueFront(MyCircularQueue* obj)
{
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->front->data;
}

2.6去尾元素

注意尾是前一个元素,所以不可以直接拿出,实在不理解看一下直接的动图

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return -1;
    //去找尾
    Node* cur= obj->front;
    while (cur->next != obj->tail)
        cur= cur->next;

    return cur->data;

}

2.7销毁

这个是我自己犯的错误

cur=cur->next,为什么不可以,因为cur等于头节点,cur等于cur->next,再释放cur,相当于把头节点next释放掉了,那我头节点后面的后面怎么去找呢?所以我们是从头节点开始释放的,把头节点用cur记录下来,释放之前让头节点走了,但是cur是头节点的傀儡节点,所以释放cur相当于是释放头节点了。

void myCircularQueueFree(MyCircularQueue* obj) {
   //和单链表的销毁一样
    Node* tmp = obj->front;
    while (obj->k + 1)
    {
        //cur=cur->next;为什么不可以
        obj->front = obj->front->next;
        free(tmp);
        tmp = obj->front;
        obj->k--;
    }
    free(obj);
}

2.8总代码

注意把判空判满提前引用!!!

typedef struct listNode {
    int data;
    struct Node* next;
}Node;

typedef struct {
    Node* front;
    Node* tail;
    int k;
}MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {

    MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    cq->front = cq->tail = (Node*)malloc(sizeof(Node));
    cq->k = k;
    //创造一个循环链表
    //这里其实已经开辟了k+1个空间了注意
    while (k)
    {
        Node* cur= (Node*)malloc(sizeof(Node));
        cur->data = 0;
        cur->next = NULL;
        cq->tail->next =cur;
        cq->tail= cq->tail->next;
        k--;
    }
    //开辟好了之后还要把尾和头发一起
    cq->tail->next =cq->front;
    cq->tail= cq->front;

    return cq;
}
//插入
//他这个题目其实是提前开辟好了,让你直接插入就可以了
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //先判满
    if (myCircularQueueIsFull(obj))
        return false;
    //直接在尾上插入
    obj->tail->data = value;
    obj->tail= obj->tail->next;
    return true;
}
//删除
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    //先判空
    if (myCircularQueueIsEmpty(obj))
        return false;
    //头删
    obj->front = obj->front->next;
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj)
{
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->front->data;
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return -1;
    //去找尾
    Node* cur= obj->front;
    while (cur->next != obj->tail)
        cur= cur->next;

    return cur->data;

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    
    return obj->front == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->tail->next == obj->front;
}

void myCircularQueueFree(MyCircularQueue* obj) {
   //和单链表的销毁一样
    Node* tmp = obj->front;
    while (obj->k + 1)
    {
        obj->front = obj->front->next;

        free(tmp);
        tmp = obj->front;

        obj->k--;
    }
    free(obj);
}


总结

用两种解法理解了循环队列,想必对链表和队列的知识做到了巩固

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

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

相关文章

Git bash获取ssh key

目录 1、获取密钥 2、查看密钥 3、在vs中向GitHub推送代码 4、重新向GitHub推送修改过的代码 1、获取密钥 指令:ssh-keygen -t rsa -C "邮箱地址" 连续按三次回车,直到出现类似以下界面: 2、查看密钥 路径:C:\U…

2024年阿里云服务器价格查询系统,最新报价

2024年腾讯云服务器优惠价格表,一张表整理阿里云服务器最新报价,阿里云服务器网整理云服务器ECS和轻量应用服务器详细CPU内存、公网带宽和系统盘详细配置报价单,大家也可以直接移步到阿里云CLUB中心查看 aliyun.club 当前最新的云服务器优惠券…

结构体类型详细讲解(附带枚举,联合)

前言: 如果你还对结构体不是很了解,那么本篇文章将会从 为什么存在结构体,结构体的优点,结构体的定义,结构体的使用与结构体的大小依次介绍,同样会附带枚举与联合体 目录 为什么存在结构体: 结构…

机器学习周报第34周

目录 摘要Abstract一、CNN复习二、目标检测2.1 背景2.2 目标检测发展脉络2.2.1传统目标检测算法2.2.2 Anchor-Based中的Two-stage目标检测算法 三、文献阅读:Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks3.1 摘要3.2 背景3.3 解…

day06vue2学习

day06 路由的封装抽离 问题:所有的路由配置都堆在main.js中不太合适么?不好,会加大代码的复杂度 目标:将路由模块抽离出来。好处:差分模块,利于维护。 大致的做法就是,将路由相关的东西都提…

关于使用TCP-S7协议读写西门子PLC字符串的问题

我们可以使用TCP-S7协议读写西门子PLC, 比如PLC中定义一个String[50] 的地址DB300.20 地址DB300.20 DB块编号为300,偏移量【地址】是30 S7协议是西门子PLC自定义的协议,默认端口102,本质仍然是TCP协议的一种具体实现&#xff…

Linux的一些基本指令

​​​​​​​ 目录 前言: 1.以指令的形式登录 2.ls指令 语法: 功能: 常用选项: 3.pwd指令 4.cd指令 4.1 绝对路径与相对路径 4.2 cd .与cd ..(注意cd后先空格,然后两个点是连一起的&#xff0…

数据库范式拆分实战

函数依赖 如果给定一个X,能唯一确定一个Y,就称X确定Y,或者说Y依赖于X,例如Y X*X函数。 X -> Y(X确定Y,Y依赖于X) 部分函数依赖 A可确定C,(A,B&#xff09…

基于深度学习的生活垃圾智能分类系统(微信小程序+YOLOv5+训练数据集+开题报告+中期检查+论文)

摘要 本文基于Python技术,搭建了YOLOv5s深度学习模型,并基于该模型研发了微信小程序的垃圾分类应用系统。本项目的主要工作如下: (1)调研了移动端垃圾分类应用软件动态,并分析其优劣势;…

stm32使用定时器实现PWM与呼吸灯

PWM介绍 STM32F103C8T6 PWM 资源: 高级定时器( TIM1 ): 7 路 通用定时器( TIM2~TIM4 ):各 4 路 例如定时器2 PWM 输出模式: PWM 模式 1 :在 向上计数 时&#xff0…

Linux之git

一、什么叫做版本控制 版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。简单来说就是用于管理多人协同开发项目的技…

【链表】Leetcode 142. 环形链表 II【中等】

环形链表 II 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系…

YoloV8改进策略:BackBone改进|PKINet

摘要 PKINet是面向遥感旋转框的主干,网络包含了CAA、PKI等模块,给我们改进卷积结构的模型带来了很多启发。本文,使用PKINet替代YoloV8的主干网络,实现涨点。PKINet是我在作者的模型基础上,重新修改了底层的模块,方便大家轻松移植到YoloV8上。 论文:《Poly Kernel Ince…

zabbix企业微信的告警媒介配置

简介: Zabbix企业微信告警媒介可用于向特定群组成员发送提醒通知。 前提条件: 完成Zabbix告警平台的搭建后,需将群机器人添加至告警提醒群中。 企业微信群聊——右上角三个点——添加群机器人 保存好产生的webhook地址(注意&…

Kotlin的lateinit关键词

Kotlin的lateinit关键词 lateinit,延迟初始化。有时,并不能定义一个变量或对象值为空,而也没办法在对象或变量在定义声明时就为它赋值初始化,那么这时就需要用到Kotlin提供的延迟初始化lateinit。比如,有些依赖注入框架…

Docker可视化管理工具DockerUI

什么是 DockerUI ? DockerUI 是一款开源的、强大的、轻量级的 Docker 管理工具。DockerUI 覆盖了 docker cli 命令行 95% 以上的命令功能,通过可视化的 Web 界面操作,可以非常方便、轻松进行 docker 环境和 docker swarm 集群环境的管理和维护…

二分算法(查找)

问题:在数组中查找某一个数字x4的下标 例:arr:1 3 4 6 10 20 21 22 显然,数字4的下标为3。 1、线性查找,一个个地去遍历,时间复杂度为O(n) 2、二分查找&#xff0…

Centos7虚拟机中oracle19c数据库安装

目录[-] 1. Centos7虚拟机中oracle19c数据库安装 1.1. 1.先诀条件1.2. 2.oracle19c安装准备(root用户下执行)1.3. 3.CentOS7上安装oracle19c 1.先诀条件 本文在安装oracle19c时,各项oracle配置操作都通过图形界面进行。因此CentOS7系统需要安装gnome图形程序,虚拟机安装时未…

【自编码器】梳理(上)

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 梳理有关自编码器 1. 自编码器 1.1 原理 Auto-Encoder,中文称作自编码器,是一种无监督式学习模型。利用输入数据 X X X本身作…

苍穹外卖笔记

苍穹外卖 DAY01nginx反向代理MD5加密yapi进行接口导入Swagger介绍 DAY02新增员工需求分析和设计写相关代码测试(1. 后端文档测试 2. 前后端联调代码完善 员工分页查询DAY01 02涉及到的知识 DAY01 nginx反向代理 MD5加密 拓展:spring security jwt 提供了更强大灵…