225. Implement Stack Using Queues - Explanation
Description
Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty).
Implement the MyStack class:
void push(int x)Pushes elementxto the top of the stack.int pop()Removes the element on the top of the stack and returns it.int top()Returns the element on the top of the stack.boolean empty()Returns true if the stack is empty, false otherwise.
Notes:
- You must use only standard operations of a queue, which means that only
pushtoback,peek/pop from front,sizeandis emptyoperations are valid. - Depending on your language, the queue may not be supported natively. You may simulate a queue using a list or deque (double-ended queue) as long as you use only a queue's standard operations.
Example 1:
Input: ["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
Output: [null, null, null, 2, 2, false]Explanation:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // return 2
myStack.pop(); // return 2
myStack.empty(); // return False
Constraints:
1 <= x <= 9- At most
100calls will be made topush,pop,top, and empty. - All the calls to
popandtopare valid.
Follow-up: Can you implement the stack using only one queue?
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Queues - Understanding FIFO (First-In-First-Out) operations including enqueue and dequeue
- Stacks - Understanding LIFO (Last-In-First-Out) behavior and why it differs from queues
- Linked Lists - Basic understanding for the queue-of-queues approach that uses a linked structure
1. Using Two Queues
Intuition
A stack follows Last-In-First-Out (LIFO) order, but a queue follows First-In-First-Out (FIFO). To simulate a stack using queues, we need to reverse the order of elements on each push.
The idea is to use two queues: when pushing a new element, we add it to the empty second queue, then move all elements from the first queue behind it. This places the newest element at the front, ready to be popped first.
After rearranging, we swap the two queues so the main queue always has elements in stack order.
Algorithm
- Initialize two empty queues
q1andq2. - push(x): Add
xtoq2, then move all elements fromq1toq2one by one. Finally, swapq1andq2. - pop(): Remove and return the front element from
q1. - top(): Return the front element of
q1without removing it. - empty(): Return
trueifq1is empty.
class MyStack:
def __init__(self):
self.q1 = deque()
self.q2 = deque()
def push(self, x: int) -> None:
self.q2.append(x)
while self.q1:
self.q2.append(self.q1.popleft())
self.q1, self.q2 = self.q2, self.q1
def pop(self) -> int:
return self.q1.popleft()
def top(self) -> int:
return self.q1[0]
def empty(self) -> bool:
return len(self.q1) == 0Time & Space Complexity
- Time complexity:
- time for initialization.
- time for each function call.
- time for each function call.
- Space complexity:
2. Using One Queue
Intuition
We can achieve the same result with just one queue. The trick is to rotate the queue after each push so the newest element moves to the front.
When we add an element, we push it to the back of the queue, then dequeue and re-enqueue all the elements that were already there. This effectively moves the new element to the front.
This approach uses less space than two queues while maintaining the same time complexity for push operations.
Algorithm
- Initialize a single empty queue
q. - push(x): Add
xto the back ofq. Then, rotate the queue by removing and re-adding elementssize - 1times. This movesxto the front. - pop(): Remove and return the front element from
q. - top(): Return the front element of
qwithout removing it. - empty(): Return
trueifqis empty.
class MyStack:
def __init__(self):
self.q = deque()
def push(self, x: int) -> None:
self.q.append(x)
for _ in range(len(self.q) - 1):
self.q.append(self.q.popleft())
def pop(self) -> int:
return self.q.popleft()
def top(self) -> int:
return self.q[0]
def empty(self) -> bool:
return len(self.q) == 0Time & Space Complexity
- Time complexity:
- time for initialization.
- time for each function call.
- time for each function call.
- Space complexity:
3. Queue Of Queues
Intuition
This approach takes a different perspective by nesting structures. Instead of rearranging elements, we create a new queue (or node) for each push that contains the value and a reference to the previous structure.
Each push wraps the current state inside a new container, with the new value at the front. This creates a chain where the most recent element is always immediately accessible.
In practice, this behaves like building a linked list where each node holds a value and points to the rest of the stack.
Algorithm
- Initialize
qasnull(empty stack). - push(x): Create a new queue/node containing
xas the first element and the oldqas the second element. Updateqto point to this new structure. - pop(): Extract the first element (the value), update
qto point to the second element (the rest of the stack), and return the value. - top(): Return the first element of
qwithout modifying anything. - empty(): Return
trueifqisnull.
class MyStack:
def __init__(self):
self.q = None
def push(self, x: int) -> None:
self.q = deque([x, self.q])
def pop(self) -> int:
top = self.q.popleft()
self.q = self.q.popleft()
return top
def top(self) -> int:
return self.q[0]
def empty(self) -> bool:
return not self.qTime & Space Complexity
- Time complexity:
- time for initialization.
- time for each function call.
- time for each function call.
- Space complexity:
Common Pitfalls
Incorrect Rotation Count in Single Queue Approach
When using one queue, after pushing a new element, the queue must be rotated exactly size - 1 times to move the new element to the front. A common mistake is rotating size times, which brings the queue back to its original state and leaves the new element at the back. The loop should run for i in range(len(q) - 1), not for i in range(len(q)).
Forgetting to Swap Queues in Two Queue Approach
In the two-queue approach, after moving all elements from q1 to q2 behind the new element, the queues must be swapped so that q1 always contains the elements in stack order. Forgetting to swap means subsequent pop() and top() operations will access the wrong queue, returning incorrect results or causing errors on an empty queue.
Sign in to join the discussion