力扣每日一题NO.622——循环队列
Posted 东条希尔薇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了力扣每日一题NO.622——循环队列相关的知识,希望对你有一定的参考价值。
题目描述
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 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);
}
以上是关于力扣每日一题NO.622——循环队列的主要内容,如果未能解决你的问题,请参考以下文章
力扣 每日一题 862. 和至少为 K 的最短子数组难度:困难,rating: 2306(前缀和+单调队列)