author: Sergei Golitsyn
link: https://leetcode.com/problems/implement-queue-using-stacks/
Implement a first in first out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push
,Ā peek
,Ā pop
, andĀ empty
).
Implement theĀ MyQueue
Ā class:
void push(int x)
Ā Pushes element x to the back of the queue.int pop()
Ā Removes the element from the front of the queue and returns it.int peek()
Ā Returns the element at the front of the queue.boolean empty()
Ā ReturnsĀtrue
Ā if the queue is empty,Āfalse
Ā otherwise.
Notes:
- You must useĀ onlyĀ standard operations of a stack, which means onlyĀ
push to top
,Āpeek/pop from top
,Āsize
, andĀis empty
Ā operations are valid. - Depending on your language, the stack may not be supported natively. You may simulate a stack using a list or deque (double-ended queue) as long as you use only a stack's standard operations.
Example 1:
Input
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
Output
[null, null, null, 1, 1, false]
Explanation
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
Solution
In this problem, I will show you only one solution. The main trick will be in pop and peak operations. But hold on. Letās do it step by step. First of all, we have to prepare a class and needed data structures:
static class MyQueue {
Stack<Integer> current;
Stack<Integer> tmp;
public MyQueue() {
current = new Stack<>();
tmp = new Stack<>();
}
static class MyQueue {
Stack<Integer> current;
Stack<Integer> tmp;
public MyQueue() {
current = new Stack<>();
tmp = new Stack<>();
}
}
And now, letās add a push method:
public void push(int x) {
current.push(x);
}
Simple and clear, just add an element into the stack.
And what about pop? For pop operation, we
have to move all elements from the current stack to the tmp stack
And before it, we should check out tmp stack. If tmp stack contains an elements we can simply return an element from this stack.
public int pop() {
if (!tmp.isEmpty()) {
return tmp.pop();
}
while (!current.isEmpty()) {
tmp.push(current.pop());
}
return tmp.pop();
}
Letās try to draw a picture.
-
push 1. ā 1
-
push 2. ā 2, 1
-
push 3. ā 3, 2, 1
-
pop. ā move to tmp -ā cur: 2, 1, tmp: 3
cur: 1, tmp: 2, 3 cur: tmp: 1, 2, 3
-
do you see it? after moving elements, all elements in the tmp stack are in the correct order.
Ahh, I was a bit shocked, but it is true. If you move elements from one stack into another, you will have elements in insert order, like in a queue.
Now, letās implement peek function:
public int peek() {
if (!tmp.isEmpty()) {
return tmp.peek();
}
while (!current.isEmpty()) {
tmp.push(current.pop());
}
return tmp.peek();
}
Same logic as in the pop method.
And finally isEmpty method. Donāt forget that we have to check two queues.
public boolean empty() {
if (!tmp.isEmpty()) {
return tmp.isEmpty();
}
return current.isEmpty();
}
Complexity Analysis
- Time complexity: AmortizedĀ O(1)O(1), Worst-caseĀ O(n)O(n). In the worst case scenario when stackĀ
s2
Ā is empty, the algorithm popsĀ nnĀ elements from stack s1 and pushesĀ nnĀ elements toĀs2
, whereĀ nnĀ is the queue size. This givesĀ 2n2nĀ operations, which isĀ O(n)O(n). But when stackĀs2
Ā is not empty the algorithm hasĀ O(1)O(1)Ā time complexity. So what does it mean by AmortizedĀ O(1)O(1)? Please see the next section on Amortized Analysis for more information. - Space complexity :Ā O(1)O(1).