Skip to main content

232. Implement Queue using Stacks

easyAsked at Twilio

Implement Queue using Stacks is Twilio's amortized-analysis probe: build a FIFO queue using only stack primitives. The grading bar is whether you reach for the two-stack trick (in-stack + out-stack) and articulate the O(1) AMORTIZED guarantee on pop/peek.

By Alex Chen, Founder, InterviewChamp.AI · Last verified

Source citations

Public interview reports confirming this problem appears in Twilio loops.

  • Glassdoor (2026-Q1)Listed in Twilio backend SWE phone-screen reports as a 'data-structure design' warm-up.
  • LeetCode Discuss (2025-12)Cited in Twilio interview reports as a precursor to harder design questions.

Problem

Implement a first-in-first-out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty). Implement the MyQueue class: void push(int x) pushes element x to the back of the queue; int pop() removes the element from the front of the queue and returns it; int peek() returns the element at the front of the queue; boolean empty() returns true if the queue is empty. Amortized time complexity should be O(1) per operation.

Constraints

  • 1 <= x <= 9
  • At most 100 calls will be made to push, pop, peek, and empty.
  • All the calls to pop and peek are valid.

Examples

Example 1

Input
push(1), push(2), peek(), pop(), empty()
Output
[null, null, 1, 1, false]

Approaches

1. Single stack + reverse on every pop (rejected)

Push to a stack; on pop/peek, drain into a second stack to reverse, pop the top, drain back.

Time
push O(1), pop/peek O(n) every call
Space
O(n)
class MyQueueBrute {
  constructor() { this.s = []; }
  push(x) { this.s.push(x); }
  pop() {
    const tmp = [];
    while (this.s.length > 1) tmp.push(this.s.pop());
    const result = this.s.pop();
    while (tmp.length > 0) this.s.push(tmp.pop());
    return result;
  }
  peek() {
    const tmp = [];
    while (this.s.length > 1) tmp.push(this.s.pop());
    const result = this.s[0];
    while (tmp.length > 0) this.s.push(tmp.pop());
    return result;
  }
  empty() { return this.s.length === 0; }
}

Tradeoff: Drains the entire stack on every pop and re-drains it back — O(n) per call with O(n) extra ops. The optimal two-stack version amortizes this work.

2. Two stacks — in + out, lazy drain (optimal)

Push to in-stack. On pop/peek, if out-stack is empty, drain in-stack into out-stack. Pop from out-stack.

Time
amortized O(1) per op
Space
O(n)
class MyQueue {
  constructor() {
    this.in = [];
    this.out = [];
  }
  push(x) { this.in.push(x); }
  _drain() {
    if (this.out.length === 0) {
      while (this.in.length > 0) this.out.push(this.in.pop());
    }
  }
  pop() {
    this._drain();
    return this.out.pop();
  }
  peek() {
    this._drain();
    return this.out[this.out.length - 1];
  }
  empty() {
    return this.in.length === 0 && this.out.length === 0;
  }
}

Tradeoff: Each element moves from in to out exactly once over its lifetime, regardless of how many pop/peek calls happen. Amortized O(1): n operations do at most 2n stack pushes/pops total.

Twilio-specific tips

Twilio reviewers specifically want the amortized-analysis narration. State 'each element moves between stacks at most twice over its lifetime — once in, once out' BEFORE writing the code. The lazy drain (only when out is empty) is the entire amortization trick — preempt the interviewer's 'why don't you drain on every push?' question by explaining that it would push the work back to O(n) per push.

Common mistakes

  • Draining out-stack into in-stack on every push — defeats the amortization.
  • Not lazy-draining (always draining on pop even if out has items) — the next pop would also drain, double-paying.
  • Forgetting to handle empty() correctly — must check BOTH stacks, not just one.

Follow-up questions

An interviewer at Twilio may pivot to one of these next:

  • Can you also implement an stack using two queues (LC 225)? (Yes — opposite construction; one approach pushes everything through.)
  • What if push needed to be O(1) AMORTIZED but pop had to be O(1) WORST-CASE? (Doesn't exist with only stacks — need a deque.)
  • How would you make this thread-safe? (Lock the whole structure or use two separate locks with care to handle the drain-stage races.)

Solve it now

Free. No sign-up. Python and JavaScript run instantly in your browser.

Output

Press Run or Cmd+Enter to execute

FAQ

Why is the amortized complexity O(1) per op?

Aggregate analysis: n push/pop calls do at most 2n total stack operations because each element enters in-stack once, moves to out-stack at most once, and leaves out-stack at most once. Total work is O(n) for n queue operations.

Why is the WORST-CASE pop still O(n)?

Because if you've pushed n elements and the out-stack is empty when you pop, that single pop call drains all n. The next n-1 pops are O(1) each, so the average is still O(1) — but the single worst pop is O(n).

Free learning resources

Curated free links for this problem.

Practice these live with InterviewChamp.AI

Drill Implement Queue using Stacks and other Twilio interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.

Practice these live with InterviewChamp.AI →