题目
标题和出处
标题:设计循环队列
出处:622. 设计循环队列
难度
6 级
题目描述
要求
设计你的循环队列实现。循环队列是一种线性数据结构,其操作基于先进先出原则并且队尾和队首相连以形成一个循环。它也被称为「环形缓冲器」。
循环队列的一个好处是可以利用这个队列之前用过的空间。在一个普通队列中,一旦队列满了,即使在队列前面仍有空间,也不能插入下一个元素。但是使用循环队列,就能使用这些空间去存储新的值。
实现 MyCircularQueue \texttt{MyCircularQueue} MyCircularQueue 类:
- MyCircularQueue(k) \texttt{MyCircularQueue(k)} MyCircularQueue(k) 初始化对象,将队列的大小设为 k \texttt{k} k。
- boolean enQueue(int value) \texttt{boolean enQueue(int value)} boolean enQueue(int value) 向循环队列插入一个元素。如果操作成功则返回 true \texttt{true} true。
- boolean deQueue() \texttt{boolean deQueue()} boolean deQueue() 从循环队列中删除一个元素。如果操作成功则返回 true \texttt{true} true。
- int Front() \texttt{int Front()} int Front() 获取队首元素。如果队列为空,返回 -1 \texttt{-1} -1。
- int Rear() \texttt{int Rear()} int Rear() 获取队尾元素。如果队列为空,返回 -1 \texttt{-1} -1。
- boolean isEmpty() \texttt{boolean isEmpty()} boolean isEmpty() 检查循环队列是否为空。
- boolean isFull() \texttt{boolean isFull()} boolean isFull() 检查循环队列是否已满。
你不允许使用内置的队列数据结构。
示例
示例 1:
输入:
["MyCircularQueue",
"enQueue",
"enQueue",
"enQueue",
"enQueue",
"Rear",
"isFull",
"deQueue",
"enQueue",
"Rear"]
\texttt{["MyCircularQueue", "enQueue", "enQueue", "enQueue", "enQueue", "Rear", "isFull", "deQueue", "enQueue", "Rear"]}
["MyCircularQueue", "enQueue", "enQueue", "enQueue", "enQueue", "Rear", "isFull", "deQueue", "enQueue", "Rear"]
[[3],
[1],
[2],
[3],
[4],
[],
[],
[],
[4],
[]]
\texttt{[[3], [1], [2], [3], [4], [], [], [], [4], []]}
[[3], [1], [2], [3], [4], [], [], [], [4], []]
输出:
[null,
true,
true,
true,
false,
3,
true,
true,
true,
4]
\texttt{[null, true, true, true, false, 3, true, true, true, 4]}
[null, true, true, true, false, 3, true, true, true, 4]
解释:
MyCircularQueue
myCircularQueue
=
new
MyCircularQueue(3);
\texttt{MyCircularQueue myCircularQueue = new MyCircularQueue(3);}
MyCircularQueue myCircularQueue = new MyCircularQueue(3);
myCircularQueue.enQueue(1);
\texttt{myCircularQueue.enQueue(1);}
myCircularQueue.enQueue(1); // 返回
True
\texttt{True}
True
myCircularQueue.enQueue(2);
\texttt{myCircularQueue.enQueue(2);}
myCircularQueue.enQueue(2); // 返回
True
\texttt{True}
True
myCircularQueue.enQueue(3);
\texttt{myCircularQueue.enQueue(3);}
myCircularQueue.enQueue(3); // 返回
True
\texttt{True}
True
myCircularQueue.enQueue(4);
\texttt{myCircularQueue.enQueue(4);}
myCircularQueue.enQueue(4); // 返回
False
\texttt{False}
False
myCircularQueue.Rear();
\texttt{myCircularQueue.Rear();}
myCircularQueue.Rear(); // 返回
3
\texttt{3}
3
myCircularQueue.isFull();
\texttt{myCircularQueue.isFull();}
myCircularQueue.isFull(); // 返回
True
\texttt{True}
True
myCircularQueue.deQueue();
\texttt{myCircularQueue.deQueue();}
myCircularQueue.deQueue(); // 返回
True
\texttt{True}
True
myCircularQueue.enQueue(4);
\texttt{myCircularQueue.enQueue(4);}
myCircularQueue.enQueue(4); // 返回
True
\texttt{True}
True
myCircularQueue.Rear();
\texttt{myCircularQueue.Rear();}
myCircularQueue.Rear(); // 返回
4
\texttt{4}
4
数据范围
- 1 ≤ k ≤ 1000 \texttt{1} \le \texttt{k} \le \texttt{1000} 1≤k≤1000
- 0 ≤ value ≤ 1000 \texttt{0} \le \texttt{value} \le \texttt{1000} 0≤value≤1000
- 最多调用 3000 \texttt{3000} 3000 次 enQueue \texttt{enQueue} enQueue、 deQueue \texttt{deQueue} deQueue、 Front \texttt{Front} Front、 Rear \texttt{Rear} Rear、 isEmpty \texttt{isEmpty} isEmpty 和 isFull \texttt{isFull} isFull
前言
循环队列的实现有两种方式,分别是基于数组和基于链表。数组和链表本身并不是环形结构,在实现时需要模拟环形结构。
解法一
思路和算法
使用数组实现循环队列时,需要维护数组、循环队列的大小(即最多允许存储的元素个数)、队首下标、队尾下标和循环队列中的元素个数。其中,数组的长度为循环队列的大小,队首下标表示队首元素所在位置的下标,队尾下标表示队尾元素的后一个位置的下标。
构造方法中,将数组 queue \textit{queue} queue 初始化为长度 k k k 的数组,将循环队列的容量 capacity \textit{capacity} capacity 设为 k k k,将队首下标 head \textit{head} head 和队尾下标 tail \textit{tail} tail 都初始化为 0 0 0,将元素个数 size \textit{size} size 初始化为 0 0 0。
对于入队操作,首先判断循环队列是否已满,如果循环队列已满则返回 false \text{false} false,如果循环队列未满则执行入队操作。
-
令 queue [ tail ] : = value \textit{queue}[\textit{tail}] := \textit{value} queue[tail]:=value。
-
令 tail : = ( tail + 1 ) m o d capacity \textit{tail} := (\textit{tail} + 1) \bmod \textit{capacity} tail:=(tail+1)modcapacity,取余运算确保 tail \textit{tail} tail 在下标范围内。
-
令 size : = size + 1 \textit{size} := \textit{size} + 1 size:=size+1。
-
返回 true \text{true} true。
对于出队操作,首先判断循环队列是否为空,如果循环队列为空则返回 false \text{false} false,如果循环队列不为空则执行出队操作。
-
令 head : = ( head + 1 ) m o d capacity \textit{head} := (\textit{head} + 1) \bmod \textit{capacity} head:=(head+1)modcapacity,取余运算确保 head \textit{head} head 在下标范围内。
-
令 size : = size − 1 \textit{size} := \textit{size} - 1 size:=size−1。
-
返回 true \text{true} true。
对于获取队首元素和获取队尾元素操作,首先判断循环队列是否为空,如果循环队列为空则返回 − 1 -1 −1,如果循环队列不为空则返回对应下标处的元素。
-
对于获取队首元素操作,返回 queue [ head ] \textit{queue}[\textit{head}] queue[head]。
-
对于获取队尾元素操作,返回 queue [ ( tail + capacity − 1 ) m o d capacity ] \textit{queue}[(\textit{tail} + \textit{capacity} - 1) \bmod \textit{capacity}] queue[(tail+capacity−1)modcapacity]。
对于检查循环队列是否为空和检查循环队列是否已满操作,这两种情况下都有 head = tail \textit{head} = \textit{tail} head=tail,因此需要根据循环队列中的元素个数判断。
-
当且仅当 size = 0 \textit{size} = 0 size=0 时,循环队列为空。
-
当且仅当 size = capacity \textit{size} = \textit{capacity} size=capacity 时,循环队列已满。
检查循环队列是否为空和检查循环队列是否已满操作可以在其余的操作中复用。具体而言,在 enQueue \texttt{enQueue} enQueue 中调用 isFull \texttt{isFull} isFull 判断循环队列是否已满,在 deQueue \texttt{deQueue} deQueue、 Front \texttt{Front} Front 和 Rear \texttt{Rear} Rear 中调用 isEmpty \texttt{isEmpty} isEmpty 判断循环队列是否为空。
代码
class MyCircularQueue {
int[] queue;
int capacity;
int head;
int tail;
int size;
public MyCircularQueue(int k) {
queue = new int[k];
capacity = k;
head = 0;
tail = 0;
size = 0;
}
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
queue[tail] = value;
tail = (tail + 1) % capacity;
size++;
return true;
}
public boolean deQueue() {
if (isEmpty()) {
return false;
}
head = (head + 1) % capacity;
size--;
return true;
}
public int Front() {
if (isEmpty()) {
return -1;
}
return queue[head];
}
public int Rear() {
if (isEmpty()) {
return -1;
}
return queue[(tail + capacity - 1) % capacity];
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == capacity;
}
}
复杂度分析
-
时间复杂度:构造方法和每一项操作的时间复杂度是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( k ) O(k) O(k),其中 k k k 是循环队列的大小。
解法二
思路和算法
使用链表实现循环队列时,需要维护队首结点、队尾结点、循环队列的大小(即最多允许存储的元素个数)和循环队列中的元素个数。
构造方法中,将队首结点 head \textit{head} head 和队尾结点 tail \textit{tail} tail 初始化为 null \text{null} null,将循环队列的容量 capacity \textit{capacity} capacity 设为 k k k,将元素个数 size \textit{size} size 初始化为 0 0 0。
对于入队操作,首先判断循环队列是否已满,如果循环队列已满则返回 false \text{false} false,如果循环队列未满则执行入队操作。
-
创建新结点 node \textit{node} node,新结点的值为 value \textit{value} value。
-
如果循环队列为空,则令 head : = node \textit{head} := \textit{node} head:=node 和 tail : = node \textit{tail} := \textit{node} tail:=node;如果循环队列不为空,则令 tail . next : = node \textit{tail}.\textit{next} := \textit{node} tail.next:=node,然后令 tail : = node \textit{tail} := \textit{node} tail:=node。
-
令 tail . next : = head \textit{tail}.\textit{next} := \textit{head} tail.next:=head,形成循环结构。
-
令 size : = size + 1 \textit{size} := \textit{size} + 1 size:=size+1。
-
返回 true \text{true} true。
对于出队操作,首先判断循环队列是否为空,如果循环队列为空则返回 false \text{false} false,如果循环队列不为空则执行出队操作。
-
令 head : = head . next \textit{head} := \textit{head}.\textit{next} head:=head.next。
-
令 tail . next : = head \textit{tail}.\textit{next} := \textit{head} tail.next:=head,形成循环结构。
-
令 size : = size − 1 \textit{size} := \textit{size} - 1 size:=size−1。
-
返回 true \text{true} true。
对于获取队首元素和获取队尾元素操作,首先判断循环队列是否为空,如果循环队列为空则返回 − 1 -1 −1,如果循环队列不为空则返回对应结点的值。
-
对于获取队首元素操作,返回 head . value \textit{head}.\textit{value} head.value。
-
对于获取队尾元素操作,返回 tail . value \textit{tail}.\textit{value} tail.value。
对于检查循环队列是否为空和检查循环队列是否已满操作,需要根据循环队列中的元素个数判断。
-
当且仅当 size = 0 \textit{size} = 0 size=0 时,循环队列为空。
-
当且仅当 size = capacity \textit{size} = \textit{capacity} size=capacity 时,循环队列已满。
检查循环队列是否为空和检查循环队列是否已满操作可以在其余的操作中复用。
代码
class MyCircularQueue {
Node head;
Node tail;
int capacity;
int size;
public MyCircularQueue(int k) {
head = null;
tail = null;
capacity = k;
size = 0;
}
public boolean enQueue(int value) {
if (isFull()) {
return false;
}
Node node = new Node(value);
if (isEmpty()) {
head = tail = node;
} else {
tail.next = node;
tail = node;
}
tail.next = head;
size++;
return true;
}
public boolean deQueue() {
if (isEmpty()) {
return false;
}
head = head.next;
tail.next = head;
size--;
return true;
}
public int Front() {
if (isEmpty()) {
return -1;
}
return head.value;
}
public int Rear() {
if (isEmpty()) {
return -1;
}
return tail.value;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == capacity;
}
}
class Node {
int value;
Node next;
public Node(int value) {
this.value = value;
}
}
复杂度分析
-
时间复杂度:构造方法和每一项操作的时间复杂度是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( k ) O(k) O(k),其中 k k k 是循环队列的大小。