循环队列的实现(附完整代码)

简介: 循环队列的实现(附完整代码)
题目解读

本题是要求我们设计一个循环的队列,循环队列要有以下功能:

1.获取队首元素,若队列为空返回-1

2.获取队尾元素,若队列为空,则返回-1

3.插入元素,插入成功返回真

4.删除元素,删除成功返回真

5.检查队列是否为空

6.检查队列是否已满

首先我们可以将之前写的用链表实现的队列的代码拷贝到该题中,以便于循环队列的实现,然后开始构思。

循环队列的解释题目中也给出了解释:

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

解题构思

所以我们可以把循环队列先画图,他是一个环形的队列,并且首位相连尾接

那么,循环队列什么时候是满的,什么时候是空的呢?

其实,当队首和队尾在同一个位置时,这个时候队列就是空的,而当对头front的位置等于对尾rear的位置加1时,这个时候队列就是满的:

经过前面的构思,这个题目就很好理解了

但是还有一个问题很值得思考:

题目中对于循环队列的定义还有一个点很重要:

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

什么意思呢?

也就是说,循环队列中我们如果在栈满了之后还想存储值,也是可以的,但是就要反复地使用之前用过的空间,会将其覆盖,所以尾指针rear和头指针front的位置的下标是会有覆盖的变化的

我们将循环队列形象地转换成数组:

这样你就能理解我上面所说的问题了!

你可以看到,队列为空时,按照题目的意思,front的位置时为rear+1的,在上图中,其实front的位置是0,rear的位置是3。

他们之间的关系就需要我们来求证一下了,因为在循环队列这个环形队列中,无论插入还是删除,都是从队头(或者是队尾)进行操作的!

我们其实就可以发现front的位置是与队列最大存储元素有关联的,上图中最大存储个数是3,当front存入4个元素时,存完第3个就满了,这个时候就应该重新从front位置开始存储,所以front(rear)和存储个数k有着以下关系:

就是说无论front的位置怎么移动,他最终都是在1-k的范围之内的

front  =  front  %  ( k + 1 )

现在,我们就可以开始用代码实现循环队列:

循环队列的构造

我们首先定义一个结构体,就是循环队列的结构

首先就是front和rear分别为队首和队尾的下标位置

然后就是k,存储元素个数

还有数组a,存储元素

typedef struct 
{
    int front;
    int rear;
    int k;
    int* a;
} 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;
}
判断循环队列是否为空

我们在前面的解题构思中就知道,当front和rear相等时,循环队列就为空了,所以我们直接返回obj->front==obj->rear,如果队列为空,就返回 1,队列不为空就返回0

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->front==obj->rear;
}
判断循环队列是否已满

当rear+1和front相等时就是满的

这里能这样写吗?答案是不能,他要除以k+1然后取余,和front的方法一样

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    return (obj->rear+1)%(obj->k+1)==obj->front;
}
循环队列插入元素

如果队列已经满了我们就直接返回false即可

如果不是满的话就要将数组rear位置下标的值赋值为你要插入的元素的值

同时rear++,然后取余,最后返回true

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;
}
循环队列删除元素

当队列为空时就不能删了,返回-1

不为空时,我们就将front的位置往前移动,这样队首的元素就被删除了

同时记得取余

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front++;
    obj->front %= (obj->k+1);   
    return true;
}
获取循环队列队首元素

这个也很简单,直接返回数组的front下标位置的元素即可

int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[obj->front];
}
获取循环队列尾首元素

返回队尾元素我们就要根据图来具体求下标的关系了

由于画图较为麻烦,作者水平很有限,故不画图,给上源码,诸位大佬自己琢磨

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}
循环队列的销毁

free循环队列目标的同时记得把数组也给free掉,不然可能会出现内存泄漏

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

完整代码如下:

typedef struct 
{
    int front;
    int rear;
    int k;
    int* a;
} 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;
}
int myCircularQueueFront(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->rear+obj->k)%(obj->k+1)];
}
void myCircularQueueFree(MyCircularQueue* obj) 
{
    free(obj->a);
    free(obj);
}

好了,今天的分享到这里就结束了,谢谢大家的支持!

相关文章
|
7月前
|
人工智能 关系型数据库 OLAP
光云科技 X AnalyticDB:构建 AI 时代下的云原生企业级数仓
AnalyticDB承载了光云海量数据的实时在线分析,为各个业务线的商家提供了丝滑的数据服务,实时物化视图、租户资源隔离、冷热分离等企业级特性,很好的解决了SaaS场景下的业务痛点,也平衡了成本。同时也基于通义+AnalyticDB研发了企业级智能客服、智能导购等行业解决方案,借助大模型和云计算为商家赋能。
597 17
|
9月前
|
机器人 API 数据安全/隐私保护
【最佳实践系列】通过AppFlow,支持飞书机器人调用阿里云百炼应用
本文介绍了如何创建并配置飞书应用及机器人,主要包括三个步骤:1. 登录飞书开发者后台,创建企业自建应用并添加机器人卡片和API权限;2. 创建AppFlow连接流,配置飞书平台凭证和阿里云百炼鉴权凭证,发布WebhookUrl,并在飞书开放平台配置事件订阅;3. 将机器人添加到群聊中,通过@机器人实现互动。以及通过AppFlow连接流集成阿里云百炼应用服务。此过程详细描述了从应用创建到机器人添加的全流程,帮助开发者快速集成飞书机器人功能。
1778 10
|
9月前
|
机器学习/深度学习 人工智能 JSON
知识蒸馏方法探究:Google Distilling Step-by-Step 论文深度分析
大型语言模型(LLM)的发展迅速,从简单对话系统进化到能执行复杂任务的先进模型。然而,这些模型的规模和计算需求呈指数级增长,给学术界和工业界带来了挑战。为解决这一问题,知识蒸馏技术应运而生,旨在将大型模型的知识转移给更小、更易管理的学生模型。Google Research 提出的“Distilling Step-by-Step”方法不仅减小了模型规模,还通过提取推理过程使学生模型在某些任务上超越教师模型。该方法通过多任务学习框架,训练学生模型同时预测标签和生成推理过程,从而实现更高效、更智能的小型化模型。这为资源有限的研究者和开发者提供了新的解决方案,推动了AI技术的普及与应用。
489 19
知识蒸馏方法探究:Google Distilling Step-by-Step 论文深度分析
|
12月前
|
编解码 前端开发 UED
探讨了CSS媒体查询在移动端开发中的应用,介绍了媒体查询的基本概念、常见条件及其在响应式布局、导航菜单、图片优化和字体调整等方面的具体应用
本文深入探讨了CSS媒体查询在移动端开发中的应用,介绍了媒体查询的基本概念、常见条件及其在响应式布局、导航菜单、图片优化和字体调整等方面的具体应用。通过实际案例分析和注意事项的讨论,旨在帮助开发者更好地理解和运用媒体查询,提升移动端用户体验。
272 4
|
机器学习/深度学习 算法 数据挖掘
最优化--梯度下降法--牛顿法(详解)
最优化--梯度下降法--牛顿法(详解)
2024 1
|
JavaScript 前端开发 程序员
Vue学习之--------Vue生命周期beforeCreate、created、beforeMount、mounted、beforeDestroy 。。。(图解详细过程)(2022/7/17)
这篇文章详细介绍了Vue的生命周期和各个阶段的钩子函数,包括`beforeCreate`、`created`、`beforeMount`、`mounted`、`beforeUpdate`、`updated`、`beforeDestroy`和`destroyed`。文章通过图解、方法说明、代码实例和测试效果,阐述了每个钩子函数的作用和使用场景,帮助读者深入理解Vue实例从创建到销毁的整个过程。
Vue学习之--------Vue生命周期beforeCreate、created、beforeMount、mounted、beforeDestroy 。。。(图解详细过程)(2022/7/17)
|
NoSQL 关系型数据库 MySQL
python协程+异步总结!
本文介绍了Python中的协程、asyncio模块以及异步编程的相关知识。首先解释了协程的概念和实现方法,包括greenlet、yield关键字、asyncio装饰器和async/await关键字。接着详细讲解了协程的意义和应用场景,如提高IO密集型任务的性能。文章还介绍了事件循环、Task对象、Future对象等核心概念,并提供了多个实战案例,包括异步Redis、MySQL操作、FastAPI框架和异步爬虫。最后提到了uvloop作为asyncio的高性能替代方案。通过这些内容,读者可以全面了解和掌握Python中的异步编程技术。
233 0
|
搜索推荐 Linux Android开发
深入解析安卓与iOS系统架构设计差异
本文旨在探讨Android和iOS两大主流操作系统在架构设计上的根本差异。通过分析两种系统的设计理念、核心组件以及实际应用表现,揭示它们如何反映不同的开发哲学和用户体验策略。我们将从系统层级结构、内存管理机制、用户界面设计三个方面入手,逐一对比Android的开放性和灵活性如何与其对手iOS的封闭性和一致性相互辉映。
|
算法 数据库 数据安全/隐私保护
铜锁探密,SM3杂凑算法加强至pro版
铜锁探密,SM3杂凑算法加强至pro版
436 0
|
网络协议 Unix
`AF_UNIX` 和 `AF_LOCAL`
`AF_UNIX` 和 `AF_LOCAL`
962 1