OJ:循环队列

622. 设计循环队列 - 力扣(LeetCode)

思路 

思路:首先循环队列的意思是:空间固定,就是提前开辟好,满了就不能插入了,但是删除数据后仍有空间,删除循环队列里面的数据后,保留它的空间,就能再插入数据,即可以重复利用之前删除的空间。

这一题的循环让我们想到一个约瑟夫环的问题,所以这题我们不加哨兵位,因为加哨兵位转的时候就可能不太好操作。

猜想:front和rear指向空(指向同一个位置),问题:得单独判断插入第一个数据的时候如何,因为你0个数据和1个数据的时候是分不清的。

对front和rear都指向空的疑问:队列有头有尾,front为头,rear为尾,两者为空指向同一节点,意味着你插入一个数据时,尾要往后走一下。这样rear有点像栈中的意思了,top意味它不是指向栈顶元素,而是指向栈顶元素的下一个。

对rear指向尾的下一个的疑问:,如果front、rear相等为空,那么满时front也等于rear,所以无法判断空和满。

因此到这里:我们最好选择rear指向尾的下一个。

改进:当然可以用size来记录链表的长度,从而区分满不满,代入接口,发现找尾不好找,所以我们能不能加一个rear的prev前驱指针从而来找尾,但是好像有点不好控制,如果这样的话,还不如直接双向链表,因此链表看起来虽然简单,但越解决越复杂。这里我们想到对于数组能够随意访问下标,我们可以尝试一下数组。

我们先来总结一下链表的点:

1.rear为尾的下一个,得改成双向才好控制。

2.rear指向最后一个,初始化和为空的时候得做特殊处理。

因此综上所述,用数组可能选择更好。

分析:rear指向尾不好,指向尾的话,初始化的话,你先初始化为0时,你是初始化的0还是插入的0是无法判断的,而且在删除的时候也要单独删除最后一个。所以我们使得rear指向尾的下一个。

其次,虽然使得rear指向尾的下一个,但是我们之前上述的疑问中所说的,空和满无法判断,这里我们称为假溢出问题。

以k=4举例,我们要多开一个空间,对多开空间而言,front等于rear即为空,因为是数组吗,我们可以直接用下标来进行访问,循环的话就用一下下标回绕的思路就好了。

rear加1回绕到front就为满。当然回绕吗,就是取模,%(k+1),对于删除来说,删除一次就往后走一次,当front等于rear时就为空。回绕的问题就解决了。

总结数组的好处:数组好找,这里rear是指向尾的下一个的,运用数组可以找到尾。

ok,我们要的条件是:front指向头、rear指向尾的下一个、多开一个空间。

当然这题已经给定空间,数组开辟好了空间之后,初始化就可以控制了,一个指向头,一个指向尾的下一个,所以相当于左闭右开。左闭右开才可以在最开始都为空,如果是左闭右闭初始化就有问题,我们在链表初始化的时候已经说过。

创建结构体 

 我们先看这个代码中给的这个结构体

typedef struct {
    
} MyCircularQueue;

 前面我们分析过,为了实现这个循环队列我们要一个front代表头,rear指向尾的下一个,当然有个整型指针用来指向我们的数据,这里我们传递个我们这个数字的有效节点。

typedef struct {
    int* a;
    int front;
    int rear;
    int k;
} MyCircularQueue;

 构建循环队列

接下来开始创建,对应创建的话,先创建整个结构体,在对里面的指针进行malloc,记住这里我们多扩一个。当然也需要初始化一下构建的结构体里面的数据。

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    int* b=(int*)malloc(sizeof(int)*(k+1));
    obj->a=b;
    obj->front=0;
    obj->rear=0;
    obj->k=k;

    return obj;
}

判空 

之后我们先写一下判断是否为空,front==rear时就为空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front==obj->rear;
}

判满 

 接下来是判断是否为满,这个时候结对考虑轮转数组的问题了,当然%(k+1)对于没有到达下标为4的没有影响(以k=4时为例)。

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->k+1)==obj->front;
}

释放 

然后我们先写一下比较好写的释放吧,就是释放obj的a,再释放obj,因为obj是指针,这里传递的也是指针,因此这里可以不用把obj置为空。

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

 接下来还有四个接口没写,分别是插入数据,删除数据,取队头元素,取队尾元素。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    
}

int myCircularQueueFront(MyCircularQueue* obj) {
    
}

int myCircularQueueRear(MyCircularQueue* obj) {
    
}

 取对头元素和取队尾元素

我们先来写取对头元素和取队尾元素两个接口吧,首先这两个我们都得先判断他们是否为空,如果为空,就直接跳出循环,取对头元素比较好取,直接用front的下标就能取到,但是对于取队尾元素呢,好像不能直接写,可能得考虑取模的情况,因为可能会遇到下面这种情况。

int myCircularQueueRear(MyCircularQueue* obj) {
     if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->rear-1+obj->k+1)%(obj->k+1)];

}

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

 插入元素和删除元素

 ok,到了这里我们还剩下插入元素和删除元素,首先对应插入元素来说,我们得先判断是否满了,如果元素满了,那么由于空间是给定的,所以无法再继续插入了。如果没有满,就先考虑下图这种情况

这时rear再最后一个,这个时候要插入元素,应该回转rear。 所以应该利用取模。而对于删除元素来说,先要判断是否为空,如果为空,那么就无法删除,就直接退出,然后这里的回转运用到了我们找队尾元素的思路。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->rear]=value;
    obj->rear=(obj->rear+1)%(obj->k+1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
   if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front=(obj->front+1)%(obj->k+1);
    return true;
}

最终代码 




typedef struct {
    int* a;
    int front;
    int rear;
    int k;
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    int* b=(int*)malloc(sizeof(int)*(k+1));
    obj->a=b;
    obj->front=0;
    obj->rear=0;
    obj->k=k;

    return obj;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front==obj->rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->k+1)==obj->front;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->rear]=value;
    obj->rear=(obj->rear+1)%(obj->k+1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
   if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->front=(obj->front+1)%(obj->k+1);
    return true;
}

int myCircularQueueRear(MyCircularQueue* obj) {
     if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->rear-1+obj->k+1)%(obj->k+1)];

}

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

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

/**
 * Your MyCircularQueue struct will be instantiated and called as such:
 * MyCircularQueue* obj = myCircularQueueCreate(k);
 * bool param_1 = myCircularQueueEnQueue(obj, value);
 
 * bool param_2 = myCircularQueueDeQueue(obj);
 
 * int param_3 = myCircularQueueFront(obj);
 
 * int param_4 = myCircularQueueRear(obj);
 
 * bool param_5 = myCircularQueueIsEmpty(obj);
 
 * bool param_6 = myCircularQueueIsFull(obj);
 
 * myCircularQueueFree(obj);
*/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值