Stacks and Queues · Stacks and Queues Fundamentals

Cross-Structure Simulation

Implementing a stack using only queue operations (or a queue using only stack operations) by choosing when to pay a reordering cost - either on every insert, or once per removal, amortized across many removals.

  • 4 min read
  • 7 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: amortized cost
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Sometimes only one kind of structure is available - a message queue, a pair of stacks, a functional list - and you need the other kind. Simulating a stack with queues or a queue with stacks is also the clearest example of where to pay for reordering: on every write, or lazily on reads.

This skill builds both, and uses them to teach amortized analysis: counting the cost of a whole sequence of operations instead of the worst single call, and knowing when that average is good enough.

We'll go from simple to more technical:

  1. The pattern: a queue from two stacks (refill only when empty) and a stack from one queue (rotate on push).
  2. A second flavor: amortized cost - why each element's moves are paid for once.
  3. Where the "when to pay" choice shows up (functional queues, caches, batching, GC).
  4. What breaks it: early refills, pouring back, forgetting peek, amortized versus worst case.

The approach

Java

// Queue from two stacks: pay lazily
Deque<Integer> in = new ArrayDeque<>(), out = new ArrayDeque<>();
void enqueue(int x) { in.push(x); }                          // always O(1)
int dequeue() {
    if (out.isEmpty()) {                                     // refill ONLY when out is empty
        while (!in.isEmpty()) out.push(in.pop());            // reversing restores FIFO order
    }
    return out.isEmpty() ? -1 : out.pop();
}

// Stack from one queue: pay on every push
Queue<Integer> q = new LinkedList<>();
void push(int x) {
    q.offer(x);
    for (int i = 0; i < q.size() - 1; i++) q.offer(q.poll());   // rotate x to the front
}
int pop() { return q.isEmpty() ? -1 : q.poll(); }

Python

from collections import deque

# Queue from two stacks: pay lazily
inbox, outbox = [], []


def enqueue(x: int) -> None:
    inbox.append(x)                                           # always O(1)


def dequeue() -> int:
    if not outbox:                                            # refill ONLY when outbox is empty
        while inbox:
            outbox.append(inbox.pop())                        # reversing restores FIFO order
    return outbox.pop() if outbox else -1


# Stack from one queue: pay on every push
q = deque()                                                   # used only as a queue: append / popleft


def push(x: int) -> None:
    q.append(x)
    for _ in range(len(q) - 1):
        q.append(q.popleft())                                 # rotate x to the front


def pop() -> int:
    return q.popleft() if q else -1

Moving everything from in to out reverses it, so the oldest element ends up on top of out. The refill must wait until out is empty: refilling early puts newer elements on top of older ones. Enqueue 1 and 2, dequeue (1), enqueue 3, and a refill now would put 3 above 2 - the next dequeue would return 3 instead of 2.

A second flavor: amortized cost

A single dequeue can move n elements - O(n). But each element is pushed to in once, moved to out once and popped once, so m operations cost O(m) in total: amortized O(1) per operation. One way to see it: charge each enqueue 3 units instead of 1 and save the extra 2 on the element; when it's later moved and popped, it pays with its savings. The stack-from-queue design makes the opposite choice - O(n) on every push, never a spike on pop - which is worse overall but has no surprises.

Where you'll see it

Functional languages build immutable queues from two lists exactly this way (the "batched queue" in Okasaki's Purely Functional Data Structures), since lists are cheap stacks. The "where to pay" choice shows up across systems: write-through versus write-back caches, batching writes into periodic flushes, and garbage collectors that trade frequent small pauses for rare large ones.

Which tool for which job?

You needUse
A FIFO queue from stackstwo stacks, refill only when the out stack is empty
A LIFO stack from a queueone queue, rotate the new item to the front on push
A queue with its minimum in O(1)two min-stacks
An immutable queue that's shared or reuseda lazy or real-time (scheduled) queue
Every operation fast, no spikesa real-time queue, or a ring buffer

Common mistakes

MistakeWhat happensDo this instead
Refilling before the out stack is emptynewer items leave before older onesrefill only when out is empty
Pouring out back into in after each dequeueO(n) per operationleave out alone until it empties
Peek without the refill stepan empty out stack hides the frontrefill in peek too
Treating amortized as worst caseone dequeue moved 34,525 itemsa real-time queue when spikes matter
Reusing old versions of a functional queuethe amortized bound breakslazy (Okasaki) queues

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why must the queue move elements from in to out only when out is empty?

Show answer

out holds older elements in the right order. Pushing newer ones on top of them lets a newer element leave first: enqueue 1, 2, dequeue, enqueue 3, and an early refill makes the next dequeue return 3 instead of 2.

2. What does "amortized O(1)" mean for the two-stack queue?

Show answer

A single dequeue can cost O(n), but any sequence of m operations costs O(m) in total, because each element is moved from in to out at most once. Averaged over the sequence, each operation is O(1).

3. Why is the stack-from-queue's push O(n)?

Show answer

After adding the new element at the back, it rotates the other n - 1 elements behind it so the newest is at the front - n - 1 dequeue/enqueue steps.

Key takeaways

  • Queue from two stacks: push to in, pop from out, refill out only when empty.
  • Each element moves once: amortized O(1) per operation, O(n) worst case for one call.
  • Stack from a queue: rotate on push - O(n) push, O(1) pop.
  • Decide where to pay: every write, or lazily on reads.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Queue from stackstwo stacks, refill when out is empty
Stack from a queuerotate on push
Queue minimumtwo min-stacks
Immutable, reusedlazy (Okasaki) queue
No spikesreal-time queue

Snippets worth knowing by heart

Java

int dequeue() {
    if (out.isEmpty()) while (!in.isEmpty()) out.push(in.pop());   // only when empty
    return out.pop();
}

Python

def push(self, x):                        # a min-stack: (value, minimum so far)
    self.items.append((x, min(x, self.items[-1][1]) if self.items else x))

The traps

  • Early refill: refilling before the out stack is empty lets newer items leave first. Refill only when out is empty.
  • Pouring back: moving items back after each dequeue makes every operation O(n). Leave out alone until it empties.
  • Peek without refill: an empty out stack hides the front. Refill in peek too.
  • Amortized isn't worst case: one dequeue can move every item. Use a real-time queue when spikes matter.

Staff-level points, one line each

  • 200,000 operations: 100,000 moves in total, but one dequeue moved 34,525.
  • Reusing one saved version 1,000 times cost 1,000,000 moves; laziness fixes it.
  • Two min-stacks make a min-queue; it matched brute-force window minimums on 300 cases.
  • Stack from a queue: 10,000 pushes cost 49,995,000 rotations.

Say it in an interview

  • "Each element is moved once, so it's amortized O(1)."
  • "A single dequeue can be O(n); for strict latency I'd use a real-time queue."
  • "Refill the out stack only when it's empty, or newer items jump the line."
  • "Two min-stacks give a queue with an O(1) minimum."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Cross-Structure Simulation free

Implement Stack using Queues

MediumProCoding problemJava · Python

Implement a stack using only queue operations internally (a Queue, not a Deque or array - in Python, a collections.deque used only through append, popleft and [0]), supporting:

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Implement Queue using Stacks

MediumProCoding problemJava · Python

Implement a queue using only stack operations internally, supporting:

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Cross-Structure Simulation free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime