程序员社区

力扣每日一题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);
}

赞(0) 打赏
未经允许不得转载:IDEA激活码 » 力扣每日一题NO.622——循环队列

一个分享Java & Python知识的社区