#每日一题 设计循环队列

力扣https://leetcode-cn.com/problems/design-circular-queue/

#每日一题 设计循环队列_第1张图片

 审题:

1.先进先出:模拟队列

2.通过头尾循环利用空间 :双向(头尾连接),题意已经暗示你用数组去模拟。

3.操作:功能函数

4.示例,便于我们写对返回值和传参

伪代码:

1.创建一个结构体,里面有指针a,有下标rear和front,(相当于头尾指针)还有k(存装有效数据的个数)  访问这些变量的时候,要用箭头指向。就是我们C++的类需要.出一样。

结构分析:这里我们其实采取的是数组模拟队列的方法。循环的关键在于,连接首与尾,同时,数组这里可以用下标模拟指针的移动,而避免顺序表中插一个挪很多的代价。

其实我们也可以看见,循环队列的意义在于:充分利用有效的空间,实现公平的流动。

2.逻辑伪代码:

(1)出队    判空;下标后移

         入队     判满;下标后移(rear下标到临界点的情况)

(2)取出队头:判空;若空返回值-1;下标前移,返回int  即数组的数据内容

         取出队尾:判空;若空返回值-1;下标前移,返回int  即数组的数据内容

(3)判断空    两指针下标相等

          判断满    两指针下标相等

(4)定义域:rear>front 恒成立          趋势:rear右移    临界点rear=k-1(rear+1 =k)     

      循环点是rear->front .    对于rear坐标到临界点的情况我们用取模的方式处理

      (obj->k+1)%(obj->k) 这个值的结果要么是0,要么是 rear+1,这是个好用的小技巧,精简         了if -else,的长代码。

3.语法细节问题  (obj->k+1)%(obj->k)    obj->k可以看成是一个整体所以不必扩,另外对于优先级来说()[]  .  ->都是一级优先级   所以注意括括号问题。这里是为了看起来整洁才括上的。

还有返回值,在给了功能函数后,我们建议先把返回值预写出来。便于我们设计结构同时也防止出错。

再写数据结构模板的时候,其实是有逻辑模板的,我们要善于利用,举一反三。比如链表的核心逻辑:插入,删除(处理内存问题),遍历(销毁链表,判空,打印),初始化列表和节点。对于顺序表来说,核心逻辑大体相似。有判满的操作,增容和下标移动。

我们可以看到本题,方法上都是对称的,我们还是要先想的非常清楚才开始去写。节省改BUG时间。

代码实现:(C语言版本) 

typedef struct {
    int *data;
    int front, rear;                                    //代表头、尾指针
    int size;
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj);      //提前声明判空判满函数
bool myCircularQueueIsFull(MyCircularQueue* obj);


MyCircularQueue* myCircularQueueCreate(int k) {
    if(k < 0)
        return NULL;
    MyCircularQueue *Q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue)); //分配空间
    Q->data = (int*)malloc(sizeof(int) * (k+1));        //需要多分配一个空间用来使尾指针指向它,防止头尾指针重合所以是k+1
    Q->front = 0;
    Q->rear = 0;
    Q->size = k+1;
    return Q;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))                      //若队列满则无法进行入队操作
        return false;
    obj->data[obj->rear] = value;
    obj->rear = (obj->rear + 1) % (obj->size);          //因为是循环队列,防止尾指针值超出数组长度
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))                     //若队列空则无法进行出对操作
        return false;
    obj->front = (obj->front + 1) % (obj->size);
    return true;
}

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    return obj->data[(obj->rear - 1 + obj->size) % (obj->size)];    //不用(obj->rear-1)是防止(obj->rear-1)为负数
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    if(obj->front == obj->rear)                         //头尾指针位置相同则代表队列为空
        return true;
    return false;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if((obj->rear + 1) % obj->size == obj->front)       //尾指针位置后一位为头指针位置则代表队列满
        return true;
    return false;
}

void myCircularQueueFree(MyCircularQueue* obj) {        //释放空间
    free(obj->data);
    free(obj);
}

                                             如有错误,请多指正!

你可能感兴趣的:(每日一题,leetcode,动态规划,算法)