题目难度: 中等
今天我们来做一道 FIFO 的设计, 这下连带前两天的 LRU 和 LFU, 把常用的内存淘汰算法补齐了 😂
大家有什么想法建议和反馈的话欢迎随时交流, 包括但不限于公众号聊天框/知乎私信评论等等~
题目描述
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
-
MyCircularQueue(k): 构造器,设置队列长度为 k 。
-
Front: 从队首获取元素。如果队列为空,返回 -1 。
-
Rear: 获取队尾元素。如果队列为空,返回 -1 。
-
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
-
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
-
isEmpty(): 检查循环队列是否为空。
-
isFull(): 检查循环队列是否已满。
-
所有的值都在 0 至 1000 的范围内;
-
操作数将在 1 至 1000 的范围内;
-
请不要使用内置的队列库。
题目样例
示例 1
-
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
题目思考
- 要求循环队列的 FIFO, 你想到了哪些数据结构可以胜任?
解决方案
方案 1
思路
- 根据题目要求, 空间要能够重复利用, 且要体现循环, 那么可以使用单向链表, 然后初始化好 front 和 rear, 再让 rear 指向 front, 这样就形成了一个环
- 因为链表本身无法统计长度, 所以需要额外维护一个 cnt, 代表当前队列中的元素个数, 用于判断 empty 或者 full
- 这样入队和出队只需要简单的移动 front 和 rear 节点到下一位, 并更新对应节点的值即可
复杂度
- 时间复杂度: 初始化需要 O(K), 每次查询和更新都只需要 O(1)
- 空间复杂度 O(K): 需要存 K 个元素
代码
Python 3
class Node:
def __init__(self, v):
self.val = v
self.nex = None
class MyCircularQueue:
def __init__(self, k: int):
"""
Initialize your data structure here. Set the size of the queue to be k.
"""
self.capacity = k
self.count = 0
self.front = Node(0)
cur = self.front
for i in range(k - 1):
nex = Node(0)
cur.nex = nex
cur = nex
self.rear = cur
self.rear.nex = self.front
def enQueue(self, value: int) -> bool:
"""
Insert an element into the circular queue. Return true if the operation is successful.
"""
if self.isFull():
return False
self.count += 1
self.rear = self.rear.nex
self.rear.val = value
return True
def deQueue(self) -> bool:
"""
Delete an element from the circular queue. Return true if the operation is successful.
"""
if self.isEmpty():
return False
self.count -= 1
self.front = self.front.nex
return True
def Front(self) -> int:
"""
Get the front item from the queue.
"""
if self.isEmpty():
return -1
return self.front.val
def Rear(self) -> int:
"""
Get the last item from the queue.
"""
if self.isEmpty():
return -1
return self.rear.val
def isEmpty(self) -> bool:
"""
Checks whether the circular queue is empty or not.
"""
return self.count == 0
def isFull(self) -> bool:
"""
Checks whether the circular queue is full or not.
"""
return self.count == self.capacity
C++
struct Node {
public:
Node() {}
Node(int v) : value {v} {}
int value {-1};
Node* next {nullptr};
};
class MyCircularQueue {
public:
/** Initialize your data structure here. Set the size of the queue to be k. */
MyCircularQueue(int k) : nodes(k) {
for (int i = 0; i < k - 1; ++i) {
nodes[i].next = &nodes[i + 1];
}
nodes[k - 1].next = &nodes[0];
front = &nodes[0];
rear = &nodes[k - 1];
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
bool enQueue(int value) {
if (isFull()) {
return false;
}
++count;
rear = rear->next;
rear->value = value;
return true;
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool deQueue() {
if (isEmpty()) {
return false;
}
--count;
front = front->next;
return true;
}
/** Get the front item from the queue. */
int Front() {
if (isEmpty()) {
return -1;
}
return front->value;
}
/** Get the last item from the queue. */
int Rear() {
if (isEmpty()) {
return -1;
}
return rear->value;
}
/** Checks whether the circular queue is empty or not. */
bool isEmpty() {
return count == 0;
}
/** Checks whether the circular queue is full or not. */
bool isFull() {
return count == nodes.size();
}
private:
int count {0};
Node* front{nullptr};
Node* rear{nullptr};
vector<Node> nodes;
};
方案 2
思路
- 回顾方案 1, 我们使用了一个单向链表来作为循环队列, 需要初始化 k 个节点, 有没有可能直接用一个数组做到?
- 答案也是可以的, 我们只需要将方案 1 中的实际节点转成对应数组的下标, 然后每次更新只需要移动下标, 如果到达末尾后就取模重新回到开头即可
复杂度
- 时间复杂度 O(1): 初始化只需要初始化一个长度为 k 的数组, 更新只是下标的移动, 查询也只是数组下标定位, 都是 O(1)
- 空间复杂度 O(K): 需要存 K 个元素
代码
Python 3
class MyCircularQueue:
def __init__(self, k: int):
"""
Initialize your data structure here. Set the size of the queue to be k.
"""
# 使用front和rear指针代表队列的两端, 使用cnt表示当前队列长度
# 注意enqueue和dequeue都是front或rear加1后然后取模
# 注意rear初始化要为-1, 使得最开始的enqueue从0开始赋值
# dequeue只需要移动front下标即可
self.q = [0] * k
self.front = 0
self.rear = -1
self.cnt = 0
def enQueue(self, value: int) -> bool:
"""
Insert an element into the circular queue. Return true if the operation is successful.
"""
if self.isFull():
return False
self.cnt += 1
self.rear = (self.rear + 1) % len(self.q)
self.q[self.rear] = value
return True
def deQueue(self) -> bool:
"""
Delete an element from the circular queue. Return true if the operation is successful.
"""
if self.isEmpty():
return False
self.cnt -= 1
self.front = (self.front + 1) % len(self.q)
return True
def Front(self) -> int:
"""
Get the front item from the queue.
"""
if self.isEmpty():
return -1
return self.q[self.front]
def Rear(self) -> int:
"""
Get the last item from the queue.
"""
if self.isEmpty():
return -1
return self.q[self.rear]
def isEmpty(self) -> bool:
"""
Checks whether the circular queue is empty or not.
"""
return self.cnt == 0
def isFull(self) -> bool:
"""
Checks whether the circular queue is full or not.
"""
return len(self.q) == self.cnt
C++
class MyCircularQueue {
public:
/** Initialize your data structure here. Set the size of the queue to be k. */
MyCircularQueue(int k) : q(k, 0) {
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
bool enQueue(int value) {
if (isFull()) {
return false;
}
++count;
rear = (rear + 1) % q.size();
q[rear] = value;
return true;
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool deQueue() {
if (isEmpty()) {
return false;
}
--count;
front = (front + 1) % q.size();
return true;
}
/** Get the front item from the queue. */
int Front() {
if (isEmpty()) {
return -1;
}
return q[front];
}
/** Get the last item from the queue. */
int Rear() {
if (isEmpty()) {
return -1;
}
return q[rear];
}
/** Checks whether the circular queue is empty or not. */
bool isEmpty() {
return count == 0;
}
/** Checks whether the circular queue is full or not. */
bool isFull() {
return count == q.size();
}
private:
int count {0};
int front{0};
int rear{-1};
vector<int> q;
};
大家可以在下面这些地方找到我~😊
我的公众号: 每日精选算法题, 欢迎大家扫码关注~😊