OJ题:设计一个循环队列

(本题取自LeetCode622.设计循环队列)

1.题目介绍

循环队列是指一种线性的数据结构,该结构遵循先进先出的原则,并且队尾则被连接在队首之后以形成一个循环。

循环队列可以循环使用队列之前用过的空间,普通队列的头元素前的节点不能再使用,而循环队列可以在队列没有满之前循环的使用空间。

OJ题:设计一个循环队列_第1张图片

 对于循环队列我们需要支持如下的操作:

1.构造一个队列长度为k的循环队列

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

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

4.向循环队列插入一个元素,若成功插入则返回true

5.从循环队列中删除一个元素,如果成功删除则返回true

6.检查队列是否为空

7.检查队列是否为满

8.释放循环队列使用的空间

2.实现思路

不难看出,循环队列的特点就是循环(经典废话),而链表利用next指针很容易就能实现这种结构。不过今天我们采用顺序表的方式来实现循环链表。

循环队列的判空条件很容易就能想到,当头尾都指向同一块空间时,这个队列就是空的。

OJ题:设计一个循环队列_第2张图片

 

 

 

而当不断插入数据时,tail向后移动,但可以看出,tail表示的是队尾的下一块空间,而非直接表示队尾。 

OJ题:设计一个循环队列_第3张图片

 所以,当队列继续插入,直到满的时候,假设tail可以回到第一个位置,此时的tail会指向哪里呢?

 

OJ题:设计一个循环队列_第4张图片

 可以看到,此时tail与head又指向了同一块空间,那么此时的问题就是,队列为空时也是这样的。如果不加以判断,就无法区分队列是满还是空了,所以这里我们有两种解决办法:

1.在队列中加一个变量size用来记住当前队列中元素的个数,当size等于0表示队列为空,当size等于k表示队列已满。

2.当队列中仍有一个空位时就判定队列已满,这样当tail指向head前一块空间时就认定队列已满,不会出现误判的情况了。

这里我们采用第一种方式解决,这种解决方式思路更加简单易懂。

3.实现过程

由于是oj题,所以如下的实现过程中 assert断言 与 判断malloc返回的指针是否有效 这里省略了,但在平常的实现中应该加上。并且head与tail所说的指向是指

 循环队列的结构

typedef struct {
int* a;//指向存储队列的空间
int head;//记录队列头的位置
int tail;//记录队列尾的位置
int k;//队列的长度
int size;//记录队列包含的元素个数
} MyCircularQueue;

这里我们需要一个指针a方便指向动态开辟的内存空间,由于k是作为参数传入的,所以我们要建立一个变量保存下来。其次还需要两个数head与tail保存队列头与尾的位置。最后还需要一个size来记录队列中包含的元素个数。 

构造循环队列

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* myqueue=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
myqueue->a=(int*)malloc(sizeof(int)*k);
myqueue->k=k;
myqueue->head=myqueue->tail=0;
myqueue->size=0;
return myqueue;
}

我们使用malloc创建一块动态的空间,并且对其成员进行初始化即可,最后返回创建好的循环队列即可。

插入元素

  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->tail]=value;
obj->tail=(obj->tail+1)%(obj->k);
obj->size++;
return true;
}

首先我们调用判断队列是否为满的函数,若队列已满则返回false。否则在队尾插入数据。由于tail指向的是队尾的下一块空间,所以在插入时直接插入元素即可,不需要先向后移动tail再插入。

然后让tail指向下一块空间即可,但是要注意,若tail已经指向第k块空间,这时候tail应该重新指向第一块空间,否则会越界。

OJ题:设计一个循环队列_第5张图片

 所以这里我们对tail+1模k,这样若tail越界,也会因为模k重新指向第一块空间,也就完成了循环的操作。

最后,size自增,完成插入,返回true。

删除元素

  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
if(myCircularQueueIsEmpty(obj))
return false;
obj->head=(obj->head+1)%(obj->k);
obj->size--;
return true;
}

同样先用函数进行判断,若队列为空则返回false。否则直接将头向前移动一位,同时注意取模。

最后size自减,完成删除,返回true。

 获取队头元素

  • Front: 从队首获取元素。如果队列为空,返回 -1 。
int myCircularQueueFront(MyCircularQueue* obj) 
{
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[obj->head];
}

用判空函数进行判断,若队列为空则返回-1,否则返回队头元素即可。

获取队尾元素

  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
int myCircularQueueRear(MyCircularQueue* obj) 
{
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->a[(obj->tail+obj->k-1)%obj->k];
}

同样,先进行判空,若队列为空返回-1,否则返回队头元素即可。

判断队列是否为空

  • isEmpty(): 检查循环队列是否为空。
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->size == 0;
}

直接利用size是否为0判断即可。

判断队列是否为满

  • isFull(): 检查循环队列是否已满。
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
return obj->size == obj->k;
}

直接利用size是否等于k即可。

释放空间

void myCircularQueueFree(MyCircularQueue* obj) 
{
free(obj->a);//释放循环队列
free(obj);//释放结构体
}

注意这里的释放不仅仅要释放结构体本身,也要释放循环队列的空间。

实现全过程

typedef struct {
int* a;
int head;
int tail;
int k;
int size;
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* myqueue=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
myqueue->a=(int*)malloc(sizeof(int)*k);
myqueue->k=k;
myqueue->head=myqueue->tail=0;
myqueue->size=0;
return myqueue;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->tail]=value;
obj->tail=(obj->tail+1)%(obj->k);
obj->size++;
return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
obj->head=(obj->head+1)%(obj->k);
obj->size--;
return true;
}

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

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

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->size == 0;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
return obj->size == obj->k;
}

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

你可能感兴趣的:(数据结构与算法,C语言,leetcode,算法)