设计循环队列

2023-11-05

前言:队列中有一种特殊的存在——环形队列,其有一定的价值与意义,这篇文章主要由一道与其相关的例题来引出相关的知识内容。

注:下述解题过程是用C语言实现。

目录

一:题目简述 

二:环形队列的简单介绍

三:环形队列的实现

(1) 数组实现

1. 过程分析

2. 代码实现

(2) 链表实现

1.过程分析

2. 代码实现


一:题目简述 

 这是来自leetcode中一道与队列相关的题,题目难度中等,这道题的实质是要实现满足循环队列的两个条件,要避免进入队列是循环状态的误区。

大家可以先自己做一做:力扣 622.设计循环队列

二:环形队列的简单介绍

队列中有一种特殊的结构——循环队列,在一些场景会进行使用,有一定的价值。

环形队列的主要特定有两个:

1.  与队列一样,数据遵循先进先出的原则——所以只能从队尾插入数据。

2.  环形队列的空间大小是一定的,也就是说它的空间可以重复利用(用数组实现时)

注意:图示只是为了能够更直观的展现一种循环队列的感觉(且仅表示数组实现),实际上数组和链表实现起来依旧是用它们自已的的物理结构,只是通过条件控制满足了循环队列的条件,从而达到了设计循环队列的效果。


三:环形队列的实现

(1) 数组实现

由循环队列的特点我们知道循环队列的实现需要判断队列是否满了,而队列为空的判断条件是队头=队尾。用数组实现环形队列时,为了防止二者条件的重复,我们需要多开辟一个不属于队列的空间来表示队尾的位置(队尾数据的下一个),而这就是数组实现循环队列的关键之处。所以数组实现循环队列的要点就是当数组越界时再返回到数组的起始位置,即需要利用取模的思想控制数组的下标从而控制数组的边界,防止数组越界并实现循环的效果。

1. 过程分析

这里再强调一下:数组一定要多开辟一个空间来满足队列为满的条件的判断。

下面再分析几个特殊的边界情况:

(1) 队列为空时,head==tail

 (2) 队列为满时,tail的下一个位置是head

数组中判断环形队列为满的条件是比较重要的一项,为了防止数组越界并恰当的判断队列为满,当(tail+1)%(数组空间大小)==head即可。

(3) tail位于数组尾部,继续插入数据 (队头删除数据head走到尾部时情况类似,也要防止越界)

(4) 获取队尾数据(红圈表示队尾数据)时,有两种情况:

特殊:当tail位于数组首位时,tail的前一位就不能表示队尾数据的下标

正常:tail不位于数组首位,tail的前一位的下标就可以表示队尾数据的下标

解决方法也有两种:直接分情况或利用取模思想获取队尾下标(下述代码中可见)。

2. 代码实现

//1. 用数组实现 (要点:利用取模的思想控制边界)
typedef struct
{
    int* a;//动态开辟数组空间
    int head;
    int tail;//head与tail都表示数组的下标
    int capacity;//表示队列的实际容量(可以表示数组最后一个位置的下标)
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);//声明

MyCircularQueue* myCircularQueueCreate(int k)//设置队列实际容量为k
{
    MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    cq->a = (int*)malloc(sizeof(int) * (k + 1));//数组要多开辟一个空间,使区分出队列空与满两种情况
    cq->head = cq->tail = 0;
    cq->capacity = k;
    return cq;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)//插入数据
{
    if (myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->a[obj->tail] = value;
        obj->tail++;
        obj->tail %= (obj->capacity + 1);//向队尾插入数据时防止越界
        return true;
    }
}

bool myCircularQueueDeQueue(MyCircularQueue* obj)//删除数据
{
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else
    {
        obj->head++;
        obj->head %= (obj->capacity + 1);//防止越界
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj)//获取对头数据
{
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->a[obj->head];
    }
}

int myCircularQueueRear(MyCircularQueue* obj)//获取队尾数据
{
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    //else   解决1(更直观)
    //{
    //   if(obj->tail == 0)   当tail位于数组的起始位置时,tail-1不是队尾的值
    //    {
    //        return obj->a[obj->capacity];//直接在数组最后一个位置插入数据
    //    }
    //    else
    //    {
    //        return obj->a[obj->tail-1];
    //    }
    //}
    else     //解决2(利用取模的思想)
    {
        //我自己实现时犯的一个错误
        //obj->tail = (obj->tail+obj->capacity) % (obj->capacity+1);
        //return obj->a[obj->tail]; err (这里只是要获取队尾数据,不能改变tail的位置)

        int i = (obj->tail + obj->capacity) % (obj->capacity + 1);
        return obj->a[i];
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj)//判断队列是否为空
{
    return obj->head == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj)//判断队列是否为满
{
    return (obj->tail + 1) % (obj->capacity + 1) == obj->head;
}

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

(2) 链表实现

1.过程分析

用链表实现循环队列实际上比数组简单一些,与数组不同的是我们可以通过计数的方式来判断队列是否为满,这样就不用开辟额外的空间。

计数方式主要表现在定义链表结构时可以多定义一个size变量来表示链表中的有效数据个数,当其等于设置好的队列空间大小时,就可以表示循环队列已满,而队列满了后也可以通过出列(删除队头元素)使size减小进而能够再次向队列中插入数据。

注意:由于链表存在循环结构,所以可能有些人会认为链表实现循环队列是通过循环链表来实现的,我这里特别说明一下,也许可以(我不知道如何实现),但是我这里实现是通过普通单链表尾插头删控制队列内元素个数的多少来实现的(与实现队列的思路相同)。并且我认为这种实现方式已经足够简单了。

2. 代码实现

//2.用单链表实现循环队列————可以想象成一个大小固定并且可以移动的队列
//用单链表实现循环队列时,可以用size与capacity来控制队列空间,当size<capacity时就可以插入数据
//中心思想:空间大小固定,队列满时不可再入数据,但是可以通过删除队头数据使队列空间不再是满的状态从而能够继续向队列内入数据,从而达到了一种循环队列的效果
//要注意:链表不是环状,只是达到了循环队列的条件!!!!!
typedef struct
{
    struct ListNode* head;
    struct ListNode* tail;
//设置size与capacity,以二者大小判断队列空间,从而判断能否插入数据(通过设置计数的方式可以判断队列空或者满)
    int size;//有效数据个数
    int capacity;//链表容量(也表示队列的容量)
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);//两个函数的声明

MyCircularQueue* myCircularQueueCreate(int k)//创建队列
{
    MyCircularQueue* cq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//队列
    cq->head = cq->tail = NULL;
    cq->size = 0;
    cq->capacity = k;//给定队列容量为k
    return cq;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)//入列
{
    if (myCircularQueueIsFull(obj))//队列满了就不可再入数据
    {
        return false;
    }
    else
    {
        struct ListNode* newnode = (struct ListNode*)malloc(sizeof(struct ListNode));
        newnode->val = value;
        newnode->next = NULL;
        if (obj->head == NULL)
        {
            obj->head = obj->tail = newnode;
        }
        else
        {
            obj->tail->next = newnode;
            obj->tail = newnode;
        }
        obj->size++;
        return true;
    }
}

bool myCircularQueueDeQueue(MyCircularQueue* obj)//出列
{
    if (myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else
    {
        struct ListNode* next = obj->head->next;
        free(obj->head);
        obj->size--;
        obj->head = next;
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj)//获取队头数据
{
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->head->val;
}

int myCircularQueueRear(MyCircularQueue* obj)//获取队尾数据
{
    if (myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->tail->val;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj)//判空
{
    return obj->size == 0;
}

bool myCircularQueueIsFull(MyCircularQueue* obj)//判满
{
    return obj->size == obj->capacity;
}

void myCircularQueueFree(MyCircularQueue* obj)//释放队列(相当于释放单链表)
{
    struct ListNode* cur = obj->head;
    while (cur)
    {
        struct ListNode* next = cur->next;
        free(cur);
        cur = next;
    }
    free(obj);
    obj = NULL;
}

总结:

设计循环队列主要是基于这道题目,我个人认为链表实现起来更为简单,但是大家可以都掌握。如各位发现了文章的错误,还望指正,谢谢。就这样,这次的分享到这里结束,再次希望各位能有所收获,再见。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计循环队列 的相关文章

  • 苹果系统与win10连接到服务器,苹果手机怎么连接win10电脑详细步骤

    使用苹果手机的朋友们你们知道苹果手机如何连接win10电脑吗 不知道的就往下看看怎么操作吧 说不定以后你可能就会用到这个方法 1 用苹果手机正品数据线连接到电脑上的 USB 端口 电脑系统会自动识别出苹果手机的内部存储器 内部存储器包括包括
  • Ai-Bot RPA自动化框架

    Ai Bot是Android Windows平台上的node js自动化框架 1 跟python的区别 跟uipaht uibot 其他框架的区别 1 VS python 相同点 Ai Bot基于node js语言的一款自动化框架 和pyt

随机推荐

  • 关于网络连接Network的使用

    开发一个局域网连接 代码 using UnityEngine using System Collections public class all MonoBehaviour private int serverPort public int
  • Python模拟超级大乐透随机选号

    看了一下体彩超级大乐透规则 前区号码由01 35共三十五个号码组成 后区号码由01 12共十二个号码组成 由此可以使用random模拟体彩超级大乐透随机选号 import random import time class Lottery 一
  • STM32串口:字节中断与帧中断不同导致的BUG

    文章目录 一 问题背景 1 1 硬件连接框图 1 2 玄学的BUG 1 3 帧中断触发条件 1 4 字节中断触发条件 二 解决问题 2 1 复现BUG 一个帧中断 2包数据 2 2 项目总结思考 在使用STM32F207这一款单片机调试串口
  • 利用华硕e6308_P8P67_PRO主板开发双目立体视觉问题小结

    利用华硕e6308 P8P67 PRO主板开发双目立体视觉问题小结 实验室有台组装计算机 主板是华硕e6308 P8P67 PRO 实验室要求我利用该计算机开发双目立体视觉系统 咋看上去 这块主板很猛 motherboard的图解如下 可以
  • Linux安装MySQL详细教程(YUM+离线安装)

    目录 1 Linux安装MySQL共有两种方法 2 软件包工具下载 离线 3 YUM安装步骤 4 离线安装 着重介绍 1 Linux安装MySQL共有两种方法 一是使用YUM 二是离线安装 2 软件包工具下载 离线 MySQL Downlo
  • RocketMQ基础概念

    目录 1 简介 2 架构 3 核心概念 1 简介 RocketMQ 是一款开源的分布式消息中间件 最初由阿里巴巴集团开发并开源 它旨在为分布式系统提供可靠 高性能 可扩展的消息通信能力 RocketMQ和RabbitMQ KAFKA一起并列
  • 前端常见的加密方式

    1 base64加密
  • 图像分割的衡量指标详解

    转载自 http m blog csdn net u011771047 article details 72777349 http blog csdn net u014593748 article details 71698246 fps
  • 常见的正则表达式

    目录 问题现象 问题分析 解决方法 拓展 1 手机号或座机 2 邮箱 3 中文 4 数字 5 英文 6 组合 问题现象 今天在项目中 发现有个正则表达式匹配不上导致了数据校验不通过的问题 如下 于是就产生了疑问 这个正则表达式有什么问题 校
  • ERROR: node with name "rabbit" already running on "localhost"

    解决方法1 这个问题是在我用 rabbitmq server start 启动rabbimq的时候出现的 说明此时还有一些rabbitmq的线程没有结束 可以使用ps ef grep rabbitmq查找到哪些rabbitmq线程没有结束
  • iOS(一)UI的appIcon和BrandAsset(LaunchImage)

    一 首先我们先来创建一个最简单的程序 点击之后 选择Single View Application 然后点击Next 输入工程名字 继续点Next
  • MQTT

    http baike baidu com link url ZHdQftpTVAOwtrvsd x23l8hH1Xj i3su2hbhD4yEkMlYXJnefijw0zjfnvKY9I5oLdRI8zxlfCiBhakD fGKq MQT
  • 百融云预计2022年净利润同比翻倍 金融AI平台头部效应尽显

    2月15日 百融云创 06608 HK 发布2022年正面盈利预告 预期于截至2022年12月31日止年度录得非国际财务报告准则溢利 净利润 约2 86亿元人民币至2 93亿元人民币 较去年同期增长约103 至108 公告显示 公司主业营收
  • 房价预测(基于决策树算法)

    预测波士顿房价 第一步 导入数据 在这个项目中 将使用波士顿房屋信息数据来训练和测试一个模型 并对模型的性能和预测能力进行评估 我们希望可以通过该模型实现对房屋的价值预估 提高房地产经纪人的工作效率 此项目的数据集来自kaggle原始数据
  • Pyhton考单词程序_考单词工具

    说明 首先我们需要一个单词表的文本文档像这样 只要单词中没有空格就行 然后运行程序 选择单词表文件 开始考单词 正确会提示Right 错误提示Wrong 一秒钟后清屏 代码 main py from module opfile import
  • [1049]since it exceeds Excel‘s limit of 65,530 URLS per worksheet

    文章目录 since it exceeds Excel s limit of 65 530 URLS per worksheet pandas 写入excel 转换Url链接的两种方法 since it exceeds Excel s li
  • el-table 动态表格 + 动态合并多列单元格方法

    动态合并单元格 之前有篇文章写了 el table 通过 span method 方法实现合并单元格的方法 但是当时只写了合并第一列的 就有小伙伴询问 如果多列合并怎么办 刚好最近有个项目遇到了动态表格并且要合并多列单元格 在详细的记录一下
  • Ubuntu17.04禁用访客模式/忽略终端大小写

    在ubuntu17 04中禁用访客模式 只需一条命令就可以了 sudo sh c printf SeatDefaults nallow guest false n gt etc lightdm lightdm conf d 50 no gu
  • python matplotlib 画图参数简要说明

    文章目录 import matplotlib pyplot as plt 用来正常显示中文 否则中文是一堆方框 plt rcParams font sans serif SimHei 用来正常显示负号 plt rcParams axes u
  • 设计循环队列

    前言 队列中有一种特殊的存在 环形队列 其有一定的价值与意义 这篇文章主要由一道与其相关的例题来引出相关的知识内容 注 下述解题过程是用C语言实现 目录 一 题目简述 二 环形队列的简单介绍 三 环形队列的实现 1 数组实现 1 过程分析