力扣:622
循环队列的数组实现:
这里我们要思考的:
第一个问题:如何让其循环起来?
那当然是插入数据至尾时,改变尾下标使其重新指向头,从头开始
第二个问题:数据类型是说明?
首先肯定数组少不了,其次需要两个下标变量,一个表示头,一个表示尾;这时候有头有尾,还不知道数组长度,就需要一个k 来表示数组长度 ,因为方便传参到其他函数,直接对k进行访问
typedef struct {
int *a;
int front;
int back;
int k;
} MyCircularQueue;
第三个问题:数组开多大?k?k+1?(此处以k=5为例)
1.数组长度为k时:
开始时,front为0,因为front表示头的下标,开始队列为空,则back也为0,此时循环队列为空,判断为空的条件就是front是否等于back,然后插入一个数据,插入到back处,然后++back;
依次类推至最后:
此时back=front,数组属于满的情况;但是上边空也是这个判断条件,就会导致尴尬
2.数组长度为K+1时:
此时back在插入最后一个数据时,还未循环到头
就可以使用back+1与front的比较条件做为满的判断
因此第三个问题便解决了,开辟K+1的数组长度
这是循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue *obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a=(int*)malloc(sizeof(int)*(k + 1));
obj->front=0;
obj->back=0;
obj->k=k;
return obj;
}
下边是判断满和空:
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front==obj->back;
}
//满的判断条件是back的下一个是front(back+1==front)
//但是不仅仅是这个,要考虑循环情况
bool myCircularQueueIsFull(MyCircularQueue* obj) {
// 对(obj->back+1) 取% 针对的是满的情况,且back恰好是最后一个
// 对于一般情况%(k+1)无影响,因为一个数%上比自己大的对自己无影响
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++;
//考虑循环,让back重新回到头
//举例为k=5:此时back++后,back为5+1=6;而k=5;k+1=6 6%6=0;等价于回到头
obj->back %= obj->k+1;
return true;
}
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
//为空,就不删,返回false
if(myCircularQueueIsEmpty(obj))
{
return false;
}
else
{
obj->front++;
//同理,考虑头删没了,循环从数组起始处开始
obj->front %= obj->k+1;
return true;
}
}
取队头队尾数据:
int myCircularQueueFront(MyCircularQueue* obj) {
//为空,没有数据返回,题中要求 返回-1
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
return obj->a[obj->front];
}
}
int myCircularQueueRear(MyCircularQueue* obj) {
//为空,没有数据返回,题中要求 返回-1
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
//法一:
//if(obj->back==0)
//{
// return obj->a[obj->k];
//}
//else
//{
// return obj->a[obj->back-1];
//}
//法二:找到尾下标,不分情况
//+(k+1)给back=0准备的,%(k+1)给一般情况准备的
return obj->a[(obj->back-1+obj->k+1)%(obj->k+1)];
}
}
销毁:
void myCircularQueueFree(MyCircularQueue* obj) {
//分两次释放,动态开辟的a数组以及循环队列
free(obj->a);
free(obj);
}
总结:
< 1 >循环队列与队列类似,只不过需要考虑back循环的判断;
< 2 >back %(数组长度)1.back小于数组长度时,无影响;2.back等于数组长度是,模完为0;即象征着回到头
< 3 >开辟数组时多开一个用于占位