力扣每日一题NO.622——循环队列

本文介绍了一种基于数组实现的循环队列数据结构,包括队列的创建、判空、判满、元素的入队和出队等核心操作,并提供了完整的C语言实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

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

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

思路求解

我们可以先画出循环队列的逻辑图

在这里插入图片描述
可以发现,它有以下几个特征

支持先进先出

空间大小固定

空间可以重复利用,即在使用过一次后,在下一轮的插入与删除,将会重新利用这个空间

声明:本文章用数组实现循环队列

模拟操作

在这里插入图片描述
为了方便操作,我们需要定义两个指针,front指向队头,tail指向队尾

为了防止判空与判满重复

我们将判空定义为front=tail

判满定义为tail+1=front

在这里插入图片描述

所以,如果我们需要k个结点来存储数据的话,我们就需要开辟k+1个单位空间,来执行判满操作

定义如下

typedef struct {
    int *a;//存储数据的数据
    int front;//头指针
    int tail;//尾指针
    int k;//队列元素个数
} MyCircularQueue;

初始化

记得在初始化数组的时候,需要多开辟一个空间

头尾指针可以先定义为0

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* pq=malloc(sizeof(MyCircularQueue));
    pq->a=malloc((k+1)*sizeof(int));
    pq->front=pq->tail=0;
    pq->k=k;
    return pq;
}

判空和判满

判空我们不难,直接front==tail即可

但是判断,不仅有tail+1==front,还有以下这种情况

在这里插入图片描述
tail再加一,就超过数组的范围了

所以我们需要利用余数的性质

tail+1后,再模运算上(k+1)就能回到数组的开头了

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

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

插入与删除

插入失败就是队列满了,直接使用判满函数即可

要插入,直接在尾部插入,然后更新下标即可,同样需要考虑在数组末尾的情况

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }

    obj->a[obj->tail]=value;
    obj->tail++;
    obj->tail%=(obj->k+1);
    return true;
}

删除的话更新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];
}

队头就有点麻烦了

我们知道队头是它指向的后一个结点

但是如果是这种情况呢

在这里插入图片描述
tail指向的元素其实是末尾

我们同样要使用余数的性质

(tail+k)%(k+1)

其中,tail+k可以跳到数组末,而(k+1)是防止数组越界

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    
    int returnTail=(obj->tail+obj->k)%(obj->k+1);
    return obj->a[returnTail];
}

完整代码

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

bool myCircularQueueIsEmpty(MyCircularQueue* obj);

bool myCircularQueueIsFull(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* pq=malloc(sizeof(MyCircularQueue));
    pq->a=malloc((k+1)*sizeof(int));
    pq->front=pq->tail=0;
    pq->k=k;
    return pq;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }

    obj->a[obj->tail]=value;
    obj->tail++;
    obj->tail%=(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;
    
    int returnTail=(obj->tail+obj->k)%(obj->k+1);
    return obj->a[returnTail];
}

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);
    obj->a=NULL;
    free(obj);
}
### 循环队列的设计解决方案 在 LeetCode 622 中,设计一个循环队列的核心在于理解其工作原理以及如何通过数组或链表实现这一数据结构。以下是关于该问题的具体分析和解决方案。 #### 数组作为基础的数据结构 为了实现循环队列的功能,通常会选择使用数组而非链表[^1]。这是因为数组提供了连续的内存分配方式,使得随机访问变得高效,并且可以通过简单的索引来操作元素。然而,由于题目要求实现的是“循环”特性,因此需要引入额外逻辑来模拟这种行为。 #### 判断队列状态的关键条件 对于任何一种形式的队列来说,“何时为空?”是一个基本而又重要的判定标准之一。当 `front` 和 `rear` 指针指向同一个位置时,则表明当前没有任何待处理项存在——即队列为完全空闲的状态[^2]。 另外还需要注意满载情况下的判断依据:如果 `(rear + 1) % (k + 1)` 结果等于 `front` 值的话,这就意味着已经达到了最大容量限制不能再继续加入新成员了[^4]。 #### 使用取模运算构建虚拟环状链接关系 尽管物理上只是简单的一维线性排列,但是借助于数学上的余数计算方法却能够创造出看似闭合回路的效果出来。具体做法是在每次更新指针变量之前先对其执行一次相应的求余操作(`%`),这样就可以保证即使越过了边界也依然能回到起始端重新开始计数下去[^3]。 下面是基于上述理论编写的一个 Python 版本的例子: ```python class MyCircularQueue: def __init__(self, k: int): self.queue = [0]*k self.headIndex = 0 self.count = 0 self.capacity = k def enQueue(self, value: int) -> bool: if self.isFull(): return False self.queue[(self.headIndex+self.count)%self.capacity]=value self.count +=1 return True def deQueue(self) -> bool: if self.isEmpty(): return False self.headIndex=(self.headIndex+1)%self.capacity self.count -=1 return True def Front(self) -> int: if self.isEmpty(): return -1 return self.queue[self.headIndex] def Rear(self) -> int: if self.isEmpty(): return -1 return self.queue[(self.headIndex+self.count-1)%self.capacity] def isEmpty(self) -> bool: return self.count ==0 def isFull(self) -> bool: return self.count == self.capacity ``` 此代码片段定义了一个名为 `MyCircularQueue` 的类,它实现了所需的所有功能并遵循了前面提到的原则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东条希尔薇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值