题目来源力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
题目:
思考:
首先它是一个队列,然后它是一个循环队列,我们可以用选择链表和数组去实现这个队列,这里我选择使用数组(为什么后面再说)
当你不论选择数组还是链表实现的时候,都需要面对一个问题,如何判断它为空?
如图:
我们可以这样玩,在数组中,让front==back的时候,就代表队列为空,我们让back始终为队尾的下一个,front为队头,
如何判断空和满
那么,我们又该如何判断满呢?
方案1.增加一个size来控制 2.多开一个控制位
选择方案1.front==back时 当size==0则为空,队列为空,size!=空则满
注意! 这里的大前提是当front==back时,去判断当前size是否==0,如图
方案2.多开一个控制位,这个意思就是让数组永远都让一个位空着,例如这样,让back不存储数据
当队列长度k为3时,我们开4个空间,push1,2,3的时候,4的位置就是空,这里强调,空格是永远指向队尾的下一个,这样的话,我们也可以轻易的将队列的下一个指向队首,实现循环
重点:在这里,隆重介绍!!!一刻都没有为难题感到困难的,而来到战场的是,%取模操作符!
这样我们只需写下 (back+1)%k+1==front 就可以回到队列首项
试想,在第一个图,当k=3 back=3的时候(这里是数组下标),4%4=0,0==front 回到队列首项
在第二个图,当k=3 back=1,2%4==2 2==front 回到队列首项
这样的话,我们也只用写下(back+1)%k+1==front 就可以判断当前队列是否已满
Push和Pop
push
push的时候我们就可以先将back当前的值存储值,再让back往后走(保持back位为空),但是,别忘了这里是循环队列,所以我们要将队尾指向队首,在这里,我们同样使用取模%操作符来执行这一步,这里我们只需要将back%=k+1即可,如图:
当前我们需要将back绕回来,back++后成为了5 5%5=0,于是回到了队列开头(数组下标0开始,不用多说了吧),同时这个式子就算back没有要循环也可以加上,因为w实际上根本不会影响,举例:当back=2 k=5的时候,2%6仍然是2,不会造成任何改变(妙啊,很妙啊)
pop
试想
我们是不是只需要将front往后走,就可以将队头删除,与pop一样,我们也需要处理循环的问题,我们在这里仍然用到%
这里我们也只用加上front%=k+1,就可以让front循环过来继续Pop
取队尾和队首
取队首:
直接取front所在的节点即可 return front,注意,这里要格外注意当队列为空的时候,要特殊判断
取队尾:
队尾的时候我们需要判断back是否已经循环到数组的前面,所以我们这里还是使用%,我们只需要将(back-1)%k+1==k+1就可以处理所有的情况(%,很神奇对吧),同样的,注意,这里要格外注意当队列为空的时候,要特殊判断
如何计算队列总长度
一般情况下,我们可能会认为back-front就是队列长度,但是我们这里需要处理循环的问题,所以我们用上size=((back+(k+1))-front)%k+1,就是队列总长度
实践开始
根据题目分析得出,我们需要对结构体内部进行如下设
typedef struct {
int* a;
int k;
int front;
int back;
} MyCircularQueue;
初始化队列
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int) * (k + 1));
obj->k = k;
obj->front =0;
obj->back = 0;
return obj;
}
注意,这里要开k+1个空间,
剩余部分
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->back;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->back + 1) % (obj->k + 1) == obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj))
{
return false;
}
else {
obj->a[obj->back] = value;
obj->back++;
obj->back %= (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;
}
return obj->a[((obj->back - 1) + (obj->k + 1)) % (obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
销毁队列
我们需要先销毁掉obj中存放数组的内存,再去销毁整个队列的内存,所以我们要先写
free(obj->a);再free(obj);