用栈实现队列
约 1016 字大约 3 分钟
2025-02-24
232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push
、pop
、peek
、empty
):
实现 MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
说明:
- 你 只能 使用标准的栈操作 —— 也就是只有
push to top
,peek/pop from top
,size
, 和is empty
操作是合法的。 - 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
示例 1:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
提示:
1 <= x <= 9
- 最多调用
100
次push
、pop
、peek
和empty
- 假设所有操作都是有效的 (例如,一个空的队列不会调用
pop
或者peek
操作)
进阶:
- 你能否实现每个操作均摊时间复杂度为
O(1)
的队列?换句话说,执行n
个操作的总时间复杂度为O(n)
,即使其中一个操作可能花费较长时间。
双栈,push栈+pop栈,pop时要判空+转移
用队列实现栈问题中,一个队列既要负责push又要负责pop,另外一个队列是辅助队列,用来存历史数据。本问题中两个栈都是主栈,不存在辅助。
之所以要用两个栈,是因为队列是先入先出的,而栈是先入后出的。
怎么办呢?再用一个栈把顺序反过来就行了。因此我们把第一个栈中的元素全转移到第二个栈中去,这样出栈的顺序就对了。
- push的时候往第一个栈里存,
- pop的时候从第二个栈里pop,pop的时候需要先判空,如果第二个栈为空, 那就把第一个栈的元素全部转移到第二个栈中再pop(即一批一批地转移), 如果不为空,直接pop,不要转移(不然会打乱批次顺序,先把这一批的输出完)。peek也是如此。
class MyQueue {
Deque<Integer> inStack; // 用于输入的栈
Deque<Integer> outStack; // 用于输出的栈
public MyQueue() {
// 初始化两个栈
inStack = new ArrayDeque<Integer>();
outStack = new ArrayDeque<Integer>();
}
// 向队列中添加一个元素
public void push(int x) {
// 直接将元素压入输入栈
inStack.push(x);
}
// 从队列中移除并返回队头元素
public int pop() {
// 如果输出栈为空,则将输入栈中的元素倒入输出栈。重点。
if (outStack.isEmpty()) {
in2out();
}
// 弹出并返回输出栈的栈顶元素
return outStack.pop();
}
// 返回队头元素
public int peek() {
// 如果输出栈为空,则将输入栈中的元素倒入输出栈。重点。
if (outStack.isEmpty()) {
in2out();
}
// 返回输出栈的栈顶元素
return outStack.peek();
}
// 检查队列是否为空
public boolean empty() {
// 只有当两个栈都为空时,队列才为空
return inStack.isEmpty() && outStack.isEmpty();
}
// 将输入栈中的元素倒入输出栈
private void in2out() {
// 当输入栈不为空时,将输入栈的栈顶元素弹出并压入输出栈
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
- 时间复杂度: push 和 empty 为 O(1) , pop 和 peek 为均摊 O(1) 。 对于每个元素,至多入栈和出栈各两次,故均摊复杂度为 O(1)。尽管在某些操作中需要将所有元素从
inStack
转移到outStack
, 这看起来可能是O(n)
的操作,但这种转移操作并不会在每次pop
或peek
时发生。 - 空间复杂度: O(n)。其中 n 是操作总数。对于有 n 次 push 操作的情况,队列中会有 n 个元素,故空间复杂度为 O(n) 。