AlgoMaster Logo

Implement Queue using Stacks

Last Updated: March 29, 2026

easy

Understanding the Problem

We need to build a queue (FIFO: first in, first out) using only stacks (LIFO: last in, first out). The challenge is that these two data structures have opposite ordering. When you push elements 1, 2, 3 onto a stack, popping gives you 3, 2, 1. But a queue should give you 1, 2, 3 in the same order they went in.

So the real question is: how do we reverse the order of elements using stacks? The key insight is surprisingly simple. If you push elements onto one stack and then pop them all into a second stack, the order reverses. The element that was at the bottom of the first stack ends up on top of the second stack, which is exactly the element that should come out first in a queue.

The interesting part of this problem is not whether you can solve it, but how efficiently you can do it. There are two main strategies, and they differ in which operation pays the cost of transferring elements between stacks.

Key Constraints:

  • 1 <= x <= 9 → The values are tiny, so there are no overflow concerns. This constraint is mostly about keeping examples simple.
  • At most 100 calls → With such a small number of operations, any approach will work performance-wise. But the follow-up asks us to think about amortized efficiency, which is the real learning goal.
  • All pop and peek calls are valid → We never need to handle the "queue is empty" edge case for these operations. This simplifies the code.

Approach 1: Push-Heavy (Eager Transfer)

Intuition

The most intuitive approach is to keep the output stack always in the correct order so that pop and peek are instant. To do this, every time we push a new element, we rearrange everything so the oldest element stays on top of the main stack.

Here is the idea: we designate one stack as our "main" stack where elements are stored in queue order (oldest on top). When a new element arrives, we move everything from the main stack to a temporary helper stack, push the new element onto the now-empty main stack, and then move everything back. The new element ends up at the bottom, and the oldest element stays on top.

Algorithm

  1. Maintain two stacks: main (stores elements in queue order, oldest on top) and helper (temporary storage during push).
  2. push(x): Move all elements from main to helper. Push x onto main. Move all elements back from helper to main.
  3. pop(): Pop from main (the top is the oldest element).
  4. peek(): Peek at main (the top is the oldest element).
  5. empty(): Check if main is empty.

Example Walkthrough

1Initial state: both stacks empty
1/7
1Initial state: helper empty
1/7

Code

This approach works but every push rearranges the entire queue. What if we only rearranged when someone actually needs to dequeue?

Approach 2: Pop-Heavy (Lazy Transfer) - Amortized O(1)

Intuition

Instead of doing all the work upfront during push, we can be lazy about it. The idea is to use two stacks with distinct roles: an input stack for pushing and an output stack for popping.

When we push, we just throw the element onto the input stack. No rearranging, no fuss. O(1).

When we pop or peek, we look at the output stack. If it has elements, the top is the oldest element, so we just pop from there. If the output stack is empty, we pour everything from the input stack into the output stack. This reversal puts the elements in FIFO order.

The genius of this approach is that each element is moved at most twice in its entire lifetime: once when it is pushed onto the input stack, and once when it is transferred to the output stack. So across n operations, the total work is O(n), making each operation amortized O(1).

Algorithm

  1. Maintain two stacks: input (receives pushed elements) and output (serves pop/peek requests).
  2. push(x): Push x onto the input stack.
  3. pop(): If the output stack is empty, transfer all elements from input to output (this reverses their order). Pop from the output stack.
  4. peek(): If the output stack is empty, transfer all elements from input to output. Return the top of the output stack without removing it.
  5. empty(): Return true only if both stacks are empty.

Example Walkthrough

1Initial: both stacks empty
1/8
1Initial: output empty
1/8

Code