活动地址: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指向队尾元素下一个位置,那么问题就来了,我们就无法区分空和满两种状态了。为什么?
如图,空和满两种状态下队头指针和队尾指针都指向同一位置。
解决方法:
- 增加一个size来计数
- 增加一个空间,满的时候永远留一个位置,比如循环队列是4个元素,就开5个空间。
rear位置的下一个位置是front就是满了的状态,front和rear指向同一位置就是空了的状态。不过要注意这是逻辑结构的结论,如图为逻辑结构队列满的状态,但是我们用的是动态顺序表来实现循环队列的,物理结构上却是另一种形态,我们的操作要基于物理结构。
如图,我们要注意如何处理,让rear下标+1后%N,N代表空间总数(包括多开的一个空间),判断结果与front下标是否相等。
结构声明和队列创建
该循环列表使用动态顺序表实现,设置队头下标和队尾下标,还有一个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);
}