Queue via Two StacksLoading saved progress…

Queue via Two Stacks

A queue via two stacks builds a first-in, first-out (FIFO) queue — where the oldest waiting element leaves first — out of nothing but two last-in, first-out (LIFO) stacks. This is the classic interview exercise known as Implement Queue using Stacks (LeetCode 232). The bridge between the two orders is a single move: popping every element off one stack and pushing it onto another reverses the sequence, so the newest-on-top of one stack becomes the oldest-on-top of the other. Your queueVia2Stacks() factory returns an object with five methods and stores its data in two plain arrays used as stacks via push and pop. See Queue for background.

Signature

function queueVia2Stacks(): {
  enqueue(x: unknown): void;      // add x to the back of the queue
  dequeue(): unknown | undefined; // remove and return the front (oldest), or undefined if empty
  peek(): unknown | undefined;    // return the front without removing it, or undefined if empty
  size(): number;                 // how many elements are queued
  isEmpty(): boolean;             // true when the queue holds nothing
};

Examples

const q = queueVia2Stacks();
q.enqueue('a');
q.enqueue('b');
q.enqueue('c');
q.dequeue(); // 'a' — oldest out first (FIFO)
q.dequeue(); // 'b'
q.peek();    // 'c' — the front, still in the queue
q.size();    // 1
// Interleaving enqueues and dequeues keeps FIFO order:
const q = queueVia2Stacks();
q.enqueue(1);
q.enqueue(2);
q.dequeue(); // 1
q.enqueue(3);
q.dequeue(); // 2 — the 3 you just added waits its turn
q.dequeue(); // 3
q.dequeue(); // undefined — empty

Notes

  • FIFO, not LIFOdequeue returns the element that has waited longest, the opposite of a stack. enqueue adds at the back; dequeue and peek read from the front.
  • Two stacks only — the storage is two arrays you touch with push and pop alone. Don't reach into the middle of an array or use shift/unshift; the whole point is to build FIFO from LIFO primitives.
  • Empty reads return undefineddequeue() and peek() on an empty queue must return undefined, not throw.
  • Amortized O(1) — each element moves between the two stacks at most once, so a run of n operations costs O(n) total even though a single dequeue can occasionally do more work.
  • Any value is valid — including 0, false, null, and undefined. To tell an empty queue apart from a queued undefined, check size() or isEmpty() rather than the return value.

FAQ

Why can two stacks act as a queue when each one is last-in, first-out?
A stack reverses the order of whatever you put in. Pouring one stack into another reverses it a second time, and two reversals cancel out, so the item that was enqueued first ends up on top of the outbox and is dequeued first. That is how two LIFO stacks combine into one FIFO queue.
What is the time complexity of enqueue and dequeue?
Both are amortized O(1). Each element is moved between the two stacks at most once over its lifetime, so any sequence of n operations does O(n) total work. A single dequeue can occasionally cost O(k) when it triggers a pour of k items, but that cost is spread across the k dequeues that follow.
Why pour into the outbox only when it is empty, instead of on every enqueue?
Pouring only when the outbox runs dry guarantees each element crosses over exactly once, which is what makes the amortized cost constant. Transferring more eagerly would reshuffle the same elements repeatedly and also scramble FIFO order by dropping newer items on top of older ones already waiting to be served.
Loading editor…