设计循环队列

news2024/11/17 9:40:16

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

注:下述解题过程是用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;
}

总结:

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

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

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

相关文章

什么是Docker?看这一篇干货文章就够了!

什么是Docker容器技术的起源容器技术 vs 虚拟机什么是容器什么是docker如何使用dockerdocker是如何工作的docker的底层实现总结作为程序员我们应怎样理解docker&#xff1f; 容器技术的起源 假设你们公司正在秘密研发下一个“今日头条”APP&#xff0c;我们姑且称为明日头条&…

ORB-SLAM3算法和代码学习——重定位Relocalization

0总述 重定位是ORB-SLAM系列保持跟踪稳定性的保障&#xff0c;从ORB-SLAM沿用至ORB-SLAM3。主要作用是在跟踪失败时&#xff0c;通过词袋向量搜索在关键帧数据库中寻找和当前帧相似的关键帧作为匹配帧&#xff0c;建立数据关联并计算当前帧的位姿&#xff0c;恢复相机的运动。…

正大国际期货:外盘短线交易九大生存准则:从亏损预期出发

一、生存是第一位 这并不是陈词滥调&#xff0c;投机是非常危险的活动。投机非并输赢那样简单&#xff0c;首要的任务是在顶峰和谷底之间的波动中生存&#xff0c;如果连生存都做不到&#xff0c;你根本就没有谈及赢的资格。 即使有了好的资金管理、正确的系统和行动的前提&a…

Ubuntu18.04下安装配置AndroidStudio软件图文教程

运行环境&#xff1a;操作系统为Ubuntu18.04&#xff0c;android-studio版本为2022.1.1.19-linux&#xff0c;Java版本为jdk8,安装路径/opt/android-studio/,当前用户为xqf222,sdk下载路径默认为/home/xqf222/Android/Sdk 详细步骤和指令如下&#xff1a; 1.安装JDK8&#xf…

VTK CT重建(一) MPR 多层面重建 四视图

除了MPR之外&#xff0c;在CT重建后处理中还有很多别的常用方法&#xff0c;包括 多层面重建&#xff08;MPR&#xff09;最大密度投影&#xff08;MIP&#xff09;最小密度投影&#xff08;MinIP&#xff09;表面阴影遮盖&#xff08;SSD&#xff09;容积漫游技术&#xff08…

go validator参数校验器自定义规则及提示(自定义异常返回提示语)

原文连接&#xff1a;https://segmentfault.com/a/1190000040445612 笔者针对参数为指针的情况做了一点小优化 这里我们用validator用来做参数校验&#xff0c;gin默认的github.com/go-playground/validator&#xff0c;可以直接使用 除此之外还有一些其他的工具也挺好用的&am…

Linux基础指令

本文已收录至《Linux知识与编程》专栏&#xff01;作者&#xff1a;ARMCSKGT演示环境&#xff1a;CentOS 7 目录 前言 正文 查看当前用户whoami 查看当前目录路径pwd 清理屏幕clear 查看目录下文件指令ls 进入目录指令cd 以树状结构显示目录文件tree 创建普通文件指令t…

Leetcode.1669 合并两个链表

题目链接 Leetcode.1669 合并两个链表 Rating : 1428 题目描述 给你两个链表 list1和 list2&#xff0c;它们包含的元素分别为 n个和 m个。 请你将 list1中下标从 a到 b的全部节点都删除&#xff0c;并将list2接在被删除节点的位置。 示例 1&#xff1a; 输入&#xff1a;li…

rtsp实时流通过rtmp推送到服务端

ffmpeg可以实现这个功能。ffmpeg支持rtsp协议&#xff0c;也支持rtmp。在这个案例中rtsp是输入&#xff0c; rtmp是输出&#xff0c;ffmpeg实现了转码的功能。下面可出一个整体思路流程图。 如图1所示&#xff1a;在获取都rtsp流以后&#xff0c;解复用&#xff08;demux&…

检测之VOC转YOLO

文章目录检测所用数据有几种文件格式&#xff0c;我们对于检测&#xff0c;将使用VOC格式做为基础&#xff0c;与其它格式的的互转实现部分如下&#xff1a;检测系列相关文章参考如下链接&#xff1a; VOC数据的结构介绍及自定义生成&#xff0c;用labelimg自已标注VOC标准数据…

Notepad++作死,国产文本编辑器Notepad--发布

作死的Notepad Notepad 和 Notepad 都是基于 Windows 的文本编辑器&#xff0c;通常用于编写和编辑纯文本文件。 这两个应用程序都是简单的轻量级程序&#xff0c;提供基本的文本编辑功能。 Notepad是一口君经常使用的一款文本编辑软件&#xff0c;用了大概10年了。 然而Not…

配置并行(RH294)

当Ansible处理playbook的时候会顺序运行每个play确定play的主机列表之后Ansible将按顺序运行每个任务一般来说&#xff0c;所有主机必须在任何主机在play中启动下一个任务之前成功完成任务理论上&#xff0c;Ansible可以同时连接到play中的所有主机来执行每项任务Ansible所进行…

​力扣解法汇总1669. 合并两个链表

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。…

解决Vue启动失败报错:Module not found: Error: Can‘t resolve ‘less-loader‘

问题描述 今天想在网上找一个好看的登录页面&#xff0c;把别人的代码引入进来之后&#xff0c;发现项目编译不了&#xff0c;并且报错了&#xff1a; Module not found: Error: Can’t resolve ‘less-loader’ 分析问题 从错误的日志就可以看出来&#xff0c;是缺少了less-…

Linux: 关于 SIGCHLD 的更多细节

僵尸进程 何为僵尸进程&#xff1f; 一个进程使用fork创建子进程&#xff0c;如果子进程退出&#xff0c;而父进程并没有调用 wait 或 waitpid获取子进程的状态信息&#xff0c;那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程成为僵尸进程的因素 子进程 先…

AOP的一点浅薄理解

AOP思想应该怎么去理解&#xff01; Aspect&#xff08;切面&#xff09;&#xff1a; Aspect 声明类似于 Java 中的类声明&#xff0c;在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。 Joint point&#xff08;连接点&#xff09;&#xff1a;表示在程序中明确定义的点…

C语言学习笔记-变量

我们知道每一个程序的运行都需要内存&#xff0c;那么C语言的变量的定义是什么含义呢&#xff1f; 假如我花了200元买了一块4G内存条&#xff0c;然后我定义了一个int a ;就意味着从这4G的内存上要拿走4个字节&#xff0c;又定义了一个int b&#xff1b;那么b同样也要从4G的内存…

【OpenGL学习】OpenGL实现 基于Phong模型的基础光照

基于Phong模型的基础光照 在本节中&#xff0c;我们将利用 Phong 光照模型来完成一个简单的光照场景的渲染。 一、Phong 光照模型 Phong光照模型是20世纪70年代被提出的一种渲染逼真图像的方法&#xff0c;模型的提出者是越南出生的计算机图形学研究员Bui Tuong Phong&#…

JavaScript中的String和自定义对象~

String对象&#xff1a; 它是 JavaScript 的一种基本的数据类型 String 对象的 length 属性声明了该字符串中的字符数&#xff0c;String 类定义了大量操作字符串的方法&#xff0c;例如从字符串中提取字符或子串&#xff0c;或者检索字符或子串 需要注意的是&#xff0c;Ja…

单行文本域,多行文本域隐藏问题

多行文本域隐藏问题 overflow: hidden; 首先是溢出隐藏&#xff0c;不可或缺 display: -webkit-box; 以弹性盒模型显示 -webkit-box-orient: vertical; 盒模型元素的排列方式 -webkit-line-clamp: 3; 显示行数 <style>.postnameStyle{font-size: 30rpx;font-weight: …