设计循环队列(LeetCode:622.设计循环队列)

目录

写在前面的话:

一,题目分析

二,各接口实现

2.1构造循环队列

2.1.1两种不同形式的队列

2.1.2顺序表实现循环队列

2.2向队列中插入元素

2.3删除队列中元素

2.4获取队尾和队首元素

2.4.1获取队尾元素

2.4.2获取队首元素

2.4.3代码实现

2.5判空判满接口

2.5.1两种做法实现

2.5.2正确做法

2.5.3代码实现

 2.6释放队列

源码实现


写在前面的话:

小伙伴大家好啊!今天小编为大家带来一篇有关循环队列的题目。虽然说对于该题目,我们是将队列的接口直接拿过来用的,但是这个题目仍旧有一定的难度。

一,题目分析

如下图题目所示,循环队列首先基于先进先出,然后队尾连接在队首形成一个循环队列。它的优势表现在,如果队列链表满了之后,我们可以直接覆盖继续增加元素。

设计循环队列(LeetCode:622.设计循环队列)_第1张图片

二,各接口实现

如下图所示,题目中要求有这些接口:

设计循环队列(LeetCode:622.设计循环队列)_第2张图片

那么接下来,我们挨个实现。

2.1构造循环队列

2.1.1两种不同形式的队列

首先我们需要构造出一个空的循环队列。这里我们采用顺序表实现,当然也可以用链表实现,如下图所示: 

设计循环队列(LeetCode:622.设计循环队列)_第3张图片

题目中要求,我们需要设置一个队列长度为 k 的一个循环队列。接下来我们的例子都以 k 为 4 来解题。 

那么我们发现,队列长度是 4 ,但是我们开辟了 5 个空间,这是因为如果后面在判断队列的空和满时,必须这样做,才能判断,否则将没法判断。那么这里我们暂时不关心这个,在后面我们会解释。

2.1.2顺序表实现循环队列

这里我们通过顺序表实现。首先,我们需要两个指针,一个 front 表示头,一个 tail 表示尾。一个数组 a,以及一个 k 表示当前队列的长度 。

typedef struct {
    //数组实现
    int front;
    int tail;
    int k;
    int *a;
} MyCircularQueue;

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

那么我们在构造队列时,需要注意的就是需要开辟 K+1 个空间,最后一个不能忘记的是将新开辟的空间返回。

2.2向队列中插入元素

首先,我们需要判断当前队列是否为满,如果当前队列为满的话,则直接返回false,否则进行插入操作。

如下代码所示,当队列不为空,然后进行插入操作时。首先我们将该元素插入尾指针 tail 表示的数组 a ,然后将尾指针自增 1 ,最后需要对 k 的值进行取模操作,因为这里表示的是循环队列,我们需要将其限制在一定的范围内。

而这里的取模也非常简单,就是将 tail 对 k+1 进行取模,但是需要注意这里只有 obj->k ,没有单独的 k 。 

设计循环队列(LeetCode:622.设计循环队列)_第4张图片

2.3删除队列中元素

对于顺序表实现的队列来说,删除元素是比较简单的,但是我们需要删除的是队头的元素,因为队列的特性就是“先进先出”。所以这里我们判断完队列中不为空的时候,只需要将 front 自增 1即可,这也是我们数组中一般进行删除的方式。

当然 一定不能忘记的是对其取余,保证 front 限制在 队列长度的范围内。

设计循环队列(LeetCode:622.设计循环队列)_第5张图片

2.4获取队尾和队首元素

那么对于获取队尾和队首元素,相对来说是比较简单的。但是这里需要注意的是,两个指针 front 和 tail 在每次删除或者插入元素之后,会自增。

以及题目中要求的。如果获取不到,则直接返回 -1。

2.4.1获取队尾元素

如下图所示,当前队尾元素是 tail 指针的前一个元素,所以这里我们需要对 tail 做一个修正之后才能得到队尾元素:(tail+k)%(k+1)

设计循环队列(LeetCode:622.设计循环队列)_第6张图片

2.4.2获取队首元素

那么对于获取队首元素是相对比较简单的。直接返回当前 front 所在的元素即可。

2.4.3代码实现

两个接口的代码实现如下所示:

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    //因为tail总是指向最后一个元素后面的空位置
    //所以需要找tail之前的那个位置上的元素
    int i=(obj->tail+obj->k)%(obj->k+1);
    return obj->a[i];
}

2.5判空判满接口

2.5.1两种做法实现

如下图,我们可以看出,之前我们多开辟一个空间的作用就有了,如果我们没有多开辟一个空间,那么最开始没有数据的时候,以及数据满的时候,front 和 tail 指向的是同一个地方。

设计循环队列(LeetCode:622.设计循环队列)_第7张图片 

这样的表示方法,我们怎样去判断当前队列是否为空或者满呢?没法判断的,所以我们需要多开辟一个空间。

2.5.2正确做法

设计循环队列(LeetCode:622.设计循环队列)_第8张图片

2.5.3代码实现

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

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

 2.6释放队列

开辟的两个指针,都需要释放哦!

源码实现

typedef struct {
    //数组实现
    int front;
    int tail;
    int k;
    int *a;
} MyCircularQueue;

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

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    //如果为满,返回false
    if(myCircularQueueIsFull(obj))
        return false;
    //否则,添加元素,然后tail后移一位,如果超出数组范围,进行取模
    obj->a[obj->tail]=value;
    obj->tail++;
    obj->tail %= (obj->k+1);

    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
     if(myCircularQueueIsEmpty(obj))
         return false;
    //有元素,删除
    //删除数组元素,不用free,只需要将其覆盖
    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;
    //因为tail总是指向最后一个元素后面的空位置
    //所以需要找tail之前的那个位置上的元素
    int i=(obj->tail+obj->k)%(obj->k+1);
    return obj->a[i];
}

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);
    free(obj);
}

好的,那么对于循环队列的题目就结束啦,如果小伙伴们有问题,可以留言评论哦! 

你可能感兴趣的:(数据结构,c语言,队列,leetcode,算法,循环队列)