Implement Queue using Stacks
Easy · Stack · Explanation and code example
Using Two Stacks (Amortized Complexity)
How it works
Instead of moving elements back after each pop, we can keep them in the second stack.
stack1 handles incoming elements (push), while stack2 holds elements in reversed order for popping.
When stack2 is empty and we need to pop, we transfer all elements from stack1 to stack2 at once.
Each element is moved at most twice (once to stack2, once when popped), giving amortized O(1) per operation.
Steps
- For
push: Push the element ontos1. - For
pop: Ifs2is empty, transfer all elements froms1tos2. Then pop froms2. - For
peek: Ifs2is empty, transfer all elements froms1tos2. Then return the top ofs2. - For
empty: Returntrueif boths1ands2are empty.
Complexity
- Time complexity:
O(1)time for initialization.O(1)time for eachpush()andempty()function calls.O(1)amortized time for eachpop()andpeek()function calls.
- Space complexity:
O(n)
Things to watch for
Checking Only One Stack for Empty
In the optimized two-stack approach, the empty() method must check if both stacks are empty, not just one. A common mistake is returning s1.isEmpty() without considering s2. Elements may exist in s2 ready to be popped even when s1 is empty. The correct check is s1.isEmpty() && s2.isEmpty().
Transferring Elements Back After Every Pop
In the brute force approach, forgetting to transfer elements back from stack2 to stack1 after a pop operation breaks subsequent operations. However, in the optimized approach, the opposite mistake is problematic: unnecessarily transferring elements back defeats the amortized O(1) complexity. The key insight is that elements in s2 are already in the correct order for popping and should remain there until exhausted.
Python code example
class MyQueue:
def __init__(self):
self.s1 = []
self.s2 = []
def push(self, x: int) -> None:
self.s1.append(x)
def pop(self) -> int:
if not self.s2:
while self.s1:
self.s2.append(self.s1.pop())
return self.s2.pop()
def peek(self) -> int:
if not self.s2:
while self.s1:
self.s2.append(self.s1.pop())
return self.s2[-1]
def empty(self) -> bool:
return max(len(self.s1), len(self.s2)) == 0
C++ code example
class MyQueue {
private:
stack<int> s1, s2;
public:
MyQueue() {}
void push(int x) {
s1.push(x);
}
int pop() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
int res = s2.top();
s2.pop();
return res;
}
int peek() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
return s2.top();
}
bool empty() {
return s1.empty() && s2.empty();
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.