算法题详解:设计循环队列

题目来源力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目:a6f81496f3bc4ac689cde5f9030652f0.png

 思考:

首先它是一个队列,然后它是一个循环队列,我们可以用选择链表和数组去实现这个队列,这里我选择使用数组(为什么后面再说)

当你不论选择数组还是链表实现的时候,都需要面对一个问题,如何判断它为空?

如图:

d401188d8a3b4a4db9f7679699374ad0.png
 

 我们可以这样玩,在数组中,让front==back的时候,就代表队列为空,我们让back始终为队尾的下一个,front为队头,

如何判断空和满

那么,我们又该如何判断满呢?

方案1.增加一个size来控制 2.多开一个控制位

选择方案1.front==back时 当size==0则为空,队列为空,size!=空则满

       注意! 这里的大前提是当front==back时,去判断当前size是否==0,如图

f3908f7705df4a4abfd67a3b64aae95b.png

 方案2.多开一个控制位,这个意思就是让数组永远都让一个位空着,例如这样,让back不存储数据

7df2832f56df468b99e3c31e44c9cfbe.png

 当队列长度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即可,如图:

028b8a6c34fd45f2a2f03630734def84.png

当前我们需要将back绕回来,back++后成为了5 5%5=0,于是回到了队列开头(数组下标0开始,不用多说了吧),同时这个式子就算back没有要循环也可以加上,因为w实际上根本不会影响,举例:当back=2 k=5的时候,2%6仍然是2,不会造成任何改变(妙啊,很妙啊)

pop

试想

我们是不是只需要将front往后走,就可以将队头删除,与pop一样,我们也需要处理循环的问题,我们在这里仍然用到%

这里我们也只用加上front%=k+1,就可以让front循环过来继续Pop

f55ec7ce70bf499fbc5c8ee5dd0024fc.png

取队尾和队首

 取队首:

直接取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);
 

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值