• 力扣OJ题讲解——循环队列


    今天我们一起来做一道关于队列的OJ题目,这是力扣题目622题,点击题目链接可以直接跳转,https://leetcode.cn/problems/design-circular-queue/

    首先,我们看到要求,需要我们实现哪些功能? 

     我们需要设置队列长度K,队首元素,队尾元素,插入元素,删除元素,判断空,判断满。那这么多接口,我们要从哪里入手呢?我们现在做题无外乎要么用顺序表的方式,要么用链表的方式,使用两者其实都可以,今天我们就用顺序表的方式实现吧。既然使用顺序表也就是数组,那么我们要考虑一点,在什么情况下这个队列为空?要确定这个循环队列为空,那就需要保证,头元素的下标和尾元素的下标相等才可以,假设我们设头元素下标为front,back为尾元素下一个位置,因为我们要区分back=0时,到底是有一个元素还是没有元素的情况。即要保证front=back时,队列为空。

    考虑了队列为空的情况,那什么时候循环队列满了呢?满了就是已经不能再放其它元素,也就是尾位置,back就要追上front了,也就是back=front吗?那我们想一想,队列为空的时候和队列为满的时候,都是front=back,我们要怎么区分这两种情况呢?

    我们不妨设置队列长度K的时候多开一个空间,即k+1,我们保证k+1个空间最多只使用k个,留出一个位置,让back+1=front时为满队列。这样就能区分队列满和队列空的情况了。

    下来我们开始做题。

    1. typedef struct {
    2. int *a;//数组
    3. int front;//队首元素下标
    4. int back;//队尾元素下标的下一个位置
    5. int k;//队列大小
    6. } MyCircularQueue;

    我们定义完结构体变量下来需要对结构题的成员初始化,即

    1. MyCircularQueue* myCircularQueueCreate(int k) {
    2. MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    3. obj->a=(int*)malloc(sizeof(int)*(k+1));
    4. obj->front=0;
    5. obj->back=0;
    6. obj->k=k;
    7. return obj;
    8. }

    为什么我们要给开k+1个空间呢?原因我们在上面讲过了,就是为了让队列满的情况时back+1=front。

    下来我们先把容易实现的接口先完成,先把什么时候为空,什么时候为满实现。

    为空

    1. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    2. return obj->front==obj->back;
    3. }

    为满

    1. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    2. return obj->back+1=obj->front;//这样做合适吗?
    3. }

     我们要考虑到,这是一个循环队列,下面这种情况能满足吗?

    back要一直往后走吗? 显然不是,这是一个循环队列,当back走到k时,下一次就要回到起点,那怎么表示呢?我们不妨这样表示,(obj->back+1)%(obj->k+1)==obj->front,这个队列一共有k+1个空间,我们知道,如果一个小的数%一个比自己大的数是不会改变值的,所以,如果back+1=k+1,此时,back就要回到起点了。所以判断队列满我们可以这样写

    1. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    2. return (obj->back+1)%(obj->k+1)==obj->front;
    3. }

     下面我们写插入接口

    这是一个bool类型,也就是要返回true和false,那什么时候要返回false呢?注意这是往队列插入元素,如果队列已经满了,我们就插不了元素了。剩下就可以正常插入了,直接让obj->a[obj->back]=value即可,再让obj->back++。注意到这里,我们还要需要检查back有没有超过队列空间的大小,即我们需要在后面让obj->back%=obj->k+1;即

    1. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    2. if(myCircularQueueIsFull(obj))
    3. return false;
    4. obj->a[obj->back]=value;
    5. obj->back++;
    6. obj->back=(obj->back)%(obj->k+1);
    7. return true;
    8. }

    队列删除

    删除接口同样是bool类型,我们要判断什么时候删除失败?当然就是队列为空的时候删除失败,我们要先检查队列是否为空,再删除,删除非常简单,直接让front++就可以了,front一直在++,有没有可能front也会超出队列的空间大小?当然有可能,所以我们也需要对front检查一下,即obj->front%=obj->k+1;

    1. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    2. if(myCircularQueueIsEmpty(obj))
    3. return false;
    4. obj->front++;
    5. obj->front=(obj->front)%(obj->k+1);
    6. return true;
    7. }

    下面就是返回对头元素

    题目里说了,如果队列为空就返回-1,这种情况我们也要处理一下,其余就直接返回obj->a[obj->front]即可。

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

     队尾元素

    同样我们也要处理一次队列为空返回-1的情况,然后直接返回obj->a[obj->back-1]可以吗?

    我们思考一下,如果back=0呢?我说的不是队列为空时,因为为空时我们已经处理了,我说的时当back已经走了至少一轮重新回到起点的情况,此时的back-1就成-1了,那我们怎么处理呢?我们要知道,这种情况下的back时已经被取模了k+1后,那我们不妨可以给back-1+k+1再给它取模k+1;即(obj->back-1)+(obj->k+1)%(obj->k+1);怎么理解这个公式,还是那句话,back-1不可能大于k+1,一个数%比他小的数值不变,(a+b)%b,如果a比b小且a是正数,那这个值是不变的,这一种情况也就对应了我们此处back!=0的情况,如果back=0,尾元素就在k的位置,那back-1就是-1,他再加上k+1再%k+1要比k+1小,所以结果就是k那个位置。

    1. int myCircularQueueRear(MyCircularQueue* obj) {
    2. if(myCircularQueueIsEmpty(obj))
    3. return -1;
    4. return obj->a[(obj->back-1+obj->k+1)%(obj->k+1)];
    5. }

    到这里这道题就大功告成了,此时我们运行,报了一个这样错误

    是因为,我们在前面调用了就很多次队列为空为满的情况,也没有声明,所以我们可以把判断队列为空,为满挪到插入接口前面就可以啦。

    好啦,本次题目就到这里了,希望大家能够多多支持,我们下次再见!

  • 相关阅读:
    BAT批处理命令启动Java打包的Jar没有指定启动类且第三方lib包在主jar外的项目
    MySQL数据库技术笔记(2)
    新思路,4.9+氧化应激相关基因构建风险模型
    鼠标在QTreeView、QTableView、QTableWidget项上移动,背景色改变
    咖啡技术培训:6款创意咖啡拿铁教程
    PY32F002B从压缩包到实现串口printf输出
    match-sorter 插件
    Pytorch笔记之分类
    2023第六届中国国际眼科医学技术推广大会/山东视力康复展
    拖拽式在线表单设计器好用吗?
  • 原文地址:https://blog.csdn.net/weixin_67131528/article/details/134544737