【C语言刷题】Leetcode622—设计循环队列

本文介绍了如何使用C语言实现LeetCode的622题——设计循环队列。讨论了循环队列的概念,以及为何选择动态顺序表来实现,并详细解释了如何处理满和空的状态,以及如何进行元素的插入、删除、获取队头和队尾元素的操作。
摘要由CSDN通过智能技术生成

活动地址:CSDN21天学习挑战赛

Leetcode622——设计循环队列

题目描述

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

链接https://leetcode.cn/problems/design-circular-queue

你的实现应该支持如下操作:

MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。

示例

MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1); // 返回 true
circularQueue.enQueue(2); // 返回 true
circularQueue.enQueue(3); // 返回 true
circularQueue.enQueue(4); // 返回 false,队列已满
circularQueue.Rear(); // 返回 3
circularQueue.isFull(); // 返回 true
circularQueue.deQueue(); // 返回 true
circularQueue.enQueue(4); // 返回 true
circularQueue.Rear(); // 返回 4


提示

  • 所有的值都在 0 至 1000 的范围内;
  • 操作数将在 1 至 1000 的范围内;
  • 请不要使用内置的队列库。

核心代码模式

typedef struct {

} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {

}

int myCircularQueueFront(MyCircularQueue* obj) {

}

int myCircularQueueRear(MyCircularQueue* obj) {

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {

}

bool myCircularQueueIsFull(MyCircularQueue* obj) {

}

void myCircularQueueFree(MyCircularQueue* 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);
*/

思路分析与代码实现(C语言)

用单链表不太好,比如说你要获取队尾元素就要遍历一遍,而用顺序表直接用队尾下标-1即可,还有就是单链表创建起来比顺序表麻烦,要一个一个结点地创建,还要全部链接起来。不如就用动态顺序表的好。
循环队列中的元素插入或删除不改变空间,采取的是覆盖的策略。其实这道题的关键是要准确判空和判满,我们这里让rear指向队尾元素下一个位置,那么问题就来了,我们就无法区分空和满两种状态了。为什么?
如图,空和满两种状态下队头指针和队尾指针都指向同一位置。
image.png

解决方法

  1. 增加一个size来计数
  2. 增加一个空间,满的时候永远留一个位置,比如循环队列是4个元素,就开5个空间。

rear位置的下一个位置是front就是满了的状态,front和rear指向同一位置就是空了的状态。不过要注意这是逻辑结构的结论,如图为逻辑结构队列满的状态,但是我们用的是动态顺序表来实现循环队列的,物理结构上却是另一种形态,我们的操作要基于物理结构。
image.png
如图,我们要注意如何处理,让rear下标+1后%N,N代表空间总数(包括多开的一个空间),判断结果与front下标是否相等。
image.png
结构声明和队列创建
该循环列表使用动态顺序表实现,设置队头下标和队尾下标,还有一个N代表动态顺序表开辟的空间总数(多开了一个)。
创建队列在堆上申请空间,动态顺序表也是,比所给的k多开一个空间,完成初始化后返回队列指针。

typedef struct {
    int* arr;
    int front;
    int rear;
    int N;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->arr = (int*)malloc((k+1)*sizeof(int));
    obj->front = obj->rear = 0;
    obj->N = k + 1;
    return obj;
}

判空和判满
front和rear相等就说明队列为空,而判断满的话要注意有两种情况,就是前面讲的。

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

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

元素入队列
满了就不让放入,返回false,没有满的话就直接往rear位置放入元素,注意要记得模等一下obj->N,这是为了控制如果rear到了空间尾部的话可以回到下标0的位置,从而实现循环。

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

元素出队列
空的话就出队列失败,返回false,不为空的话就让front向前移动一位,注意要记得模等一下obj->N,这是为了控制如果rear到了空间尾部的话可以回到下标0的位置,从而实现循环。

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

获取队头元素
先看看队列是否为空,如果为空就返回-1,不为空的话直接返回队头元素即可。

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

获取队尾元素
先看看队列是否为空,如果为空就返回-1,不为空的话有两种情况要注意一下:rear为0和rear不为0的情况。
有两种写法:

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else if(obj->rear == 0)
        return obj->arr[obj->N - 1];
    else
        return obj->arr[obj->rear - 1];
}

销毁释放队列
动态开辟的结构有两个,一个是队列结构,另一个是动态数组,要先释放数组再释放结构。

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    obj->arr = NULL;
    obj->front = obj->rear = obj->N = 0;
    free(obj);
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

桦秋静

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值