225. Implement Stack Using Queues - Explanation

Problem Link

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 element x to 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 push to back, peek/pop from front, size and is empty operations 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 100 calls will be made to push, pop, top, and empty.
  • All the calls to pop and top are valid.

Follow-up: Can you implement the stack using only one queue?



Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



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

  1. Initialize two empty queues q1 and q2.
  2. push(x): Add x to q2, then move all elements from q1 to q2 one by one. Finally, swap q1 and q2.
  3. pop(): Remove and return the front element from q1.
  4. top(): Return the front element of q1 without removing it.
  5. empty(): Return true if q1 is 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) == 0

Time & Space Complexity

  • Time complexity:
    • O(1)O(1) time for initialization.
    • O(n)O(n) time for each push()push() function call.
    • O(1)O(1) time for each pop()pop() function call.
  • Space complexity: O(n)O(n)

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

  1. Initialize a single empty queue q.
  2. push(x): Add x to the back of q. Then, rotate the queue by removing and re-adding elements size - 1 times. This moves x to the front.
  3. pop(): Remove and return the front element from q.
  4. top(): Return the front element of q without removing it.
  5. empty(): Return true if q is 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) == 0

Time & Space Complexity

  • Time complexity:
    • O(1)O(1) time for initialization.
    • O(n)O(n) time for each push()push() function call.
    • O(1)O(1) time for each pop()pop() function call.
  • Space complexity: O(n)O(n)

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

  1. Initialize q as null (empty stack).
  2. push(x): Create a new queue/node containing x as the first element and the old q as the second element. Update q to point to this new structure.
  3. pop(): Extract the first element (the value), update q to point to the second element (the rest of the stack), and return the value.
  4. top(): Return the first element of q without modifying anything.
  5. empty(): Return true if q is null.
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.q

Time & Space Complexity

  • Time complexity:
    • O(1)O(1) time for initialization.
    • O(1)O(1) time for each push()push() function call.
    • O(1)O(1) time for each pop()pop() function call.
  • Space complexity: O(n)O(n)

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.