Implement Queue using Stacks — Coding Interview Walkthrough
The core approach: Implement Queue Using Stacks is solved with two stacks in O(1) amortized time per operation. Push always goes into the input stack. On dequeue or peek, if the output stack is empty, pour all elements from the input stack into it, reversing their order to simulate FIFO. Each element is moved at most once.
Implement Queue using Stacks is one of those problems where interviewers care more about whether you can explain amortized O(1) than whether you can write the code. The solution is short, but the amortized argument — why each element is transferred at most once across its lifetime — is what separates candidates who memorized the pattern from those who actually understand it.
Already know how to solve Implement Queue Using Stacks? Try a Implement Queue Using Stacks mock interview on Intervu →
The Problem
Implement a first-in-first-out (FIFO) queue using only two stacks. The queue must support push, pop, peek, and empty, all with amortized O(1) time.
Example
MyQueue q = new MyQueue();
q.push(1); q.push(2);
q.peek(); // → 1
q.pop(); // → 1
q.empty(); // → false
What Interviewers Are Actually Testing
| Skill | What Interviewers Observe |
|---|---|
| Two-stack insight | Do you know to use an inbox and outbox stack? |
| Amortized complexity | Can you explain why O(1) amortized holds even though a single pop can be O(n)? |
| Lazy transfer | Do you only transfer when the outbox is empty, not on every operation? |
| Design clarity | Is the code well-organized with clear separation of concerns? |
Step 1: Clarifying Questions
- What operations must be O(1)? Amortized O(1) is allowed — confirm.
- Is
popcalled on an empty queue? Assume valid operations per constraints.
Step 2: The Naive (O(n)) Approach
On every push, transfer all elements from stack1 to stack2, push the new element to stack2, transfer everything back to stack1. FIFO maintained, but O(n) per push. Not acceptable.
Step 3: The Key Insight
Use two stacks: an inbox (in_stack) and an outbox (out_stack). Push always goes to the inbox. Pop reads from the outbox. Only when the outbox is empty do you transfer from inbox to outbox. Each element is transferred at most once, giving amortized O(1) per operation.
---
Step 4: Optimal Strategy
- Push: append to
in_stack. O(1). - Pop/Peek: if
out_stackis empty, transfer all elements fromin_stacktoout_stack(this reverses their order, making the oldest element the top). Then pop/peek fromout_stack. - Empty: both stacks are empty.
Python Implementation
class MyQueue:
def __init__(self):
self.in_stack = [] # Receives new elements
self.out_stack = [] # Serves old elements in FIFO order
def push(self, x: int) -> None:
self.in_stack.append(x)
def _transfer(self) -> None:
"""Move all elements from in_stack to out_stack (reverses order)."""
if not self.out_stack:
while self.in_stack:
self.out_stack.append(self.in_stack.pop())
def pop(self) -> int:
self._transfer()
return self.out_stack.pop()
def peek(self) -> int:
self._transfer()
return self.out_stack[-1]
def empty(self) -> bool:
return not self.in_stack and not self.out_stack
Time & Space Complexity
| Operation | Amortized | Worst Case |
|---|---|---|
| push | O(1) | O(1) |
| pop | O(1) | O(n) |
| peek | O(1) | O(n) |
| empty | O(1) | O(1) |
Amortized argument: Each element is pushed once → O(1). Each element is transferred at most once → O(1). Each element is popped once → O(1). Total work per element: O(1). Any single pop that triggers a transfer is O(n), but those n elements were each pushed once, so the amortized cost per element is still O(1).
Common Interview Mistakes
-
Transferring on every push. This gives O(n) push instead of amortized O(1) pop — same total work, but worse distribution and missing the insight.
-
Not explaining amortized complexity. Just saying “O(1)” without the amortized argument suggests you don’t understand why.
-
Not implementing
_transferas a helper. Duplicating the transfer logic inpopandpeekleads to bugs. -
Wrong empty check. The queue is empty only when both stacks are empty — not just one.
What a Strong Candidate Sounds Like
“I’ll use two stacks: an inbox and an outbox. Push always goes to the inbox. Pop reads from the outbox. I only transfer from inbox to outbox when the outbox is empty. Each element is transferred at most once — pushed once, transferred once, popped once — so the amortized cost is O(1) per operation even though a single pop can trigger an O(n) transfer.”
Example Interview Dialogue
Interviewer: Walk me through a scenario where a pop takes O(n).
Candidate: Say I push 5 elements. The inbox has [1,2,3,4,5]. When I call pop for the first time, the outbox is empty, so I transfer all 5 — the outbox becomes [5,4,3,2,1]. That transfer was O(5). But now the next 4 pops are O(1) each — I just pop from the outbox. Over 5 operations, the total work is 5+4 = 9, which is still O(1) amortized per element.
Interviewer: Why is lazy transfer better than eager transfer?
Candidate: Eager transfer — moving everything on every push — does O(n) work per push. Lazy transfer batches the work: each element moves from inbox to outbox exactly once. The total work across all operations is O(n), making every individual operation O(1) amortized.
Related Problems to Practice
- Min Stack — Design with auxiliary state.
- Valid Parentheses — Stack fundamentals.
- Basic Calculator — Stack-based expression evaluation.
Frequently Asked Questions
How do you implement a Queue using Stacks?
A Queue employs FIFO (First-In-First-Out) logic, whereas a Stack employs LIFO (Last-In-First-Out). By utilizing two independent stacks — an “enqueue” stack and a “dequeue” stack — you can seamlessly reverse the order of operations by pouring items back and forth between them.
What is the amortized time complexity of the enqueue/dequeue operations?
While popping from the queue occasionally requires dumping elements into the alternative stack in an O(N) burst, the mathematical amortized cost evaluates purely to O(1) time per operation overall.
Practice This in a Mock Interview
The amortized O(1) argument is the make-or-break moment in this problem. Practice explaining it out loud — interviewers listen for whether you truly understand per-element accounting or just memorized the answer.
Start a mock interview for Implement Queue using Stacks on Intervu.
Further reading:
- How to Prepare for a Coding Interview in 2026, the complete roadmap
- Why LeetCode alone isn’t enough, and what to practice instead
- Practice any LeetCode problem as a live mock interview
- The Grind 75 Pathway, a structured study plan with AI practice links
- Browse all walkthroughs on GitHub, condensed solutions with code