Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA)  Module 8 – Stacks, queues and deques

Design: a min stack and a queue using two stacks

A stack that returns its minimum in O(1) and a queue made of two stacks: two original design problems, the brute force for each, and what to notice.

  • Intermediate
  • 30 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 8 of 30 minutes free
Free: The problems: the cheapest fare and a queue of print jobs · The brute forces: scanning and pouring. With a pass: the other 10 sections and 3 interview answers, about 22 minutes.

What you will learn

  • Build a stack that returns its minimum in O(1)
  • Implement a queue with two stacks in amortised O(1)
  • Implement a stack with queues and compare where each design pays
  • Test a data structure with random operations against a reference

Before you start

What you’ll learn · Before you start

What you will learn

  • Build a stack that returns its minimum in O(1)
  • Implement a queue with two stacks in amortised O(1)
  • Implement a stack with queues and compare where each design pays
  • Test a data structure with random operations against a reference

Before you start

On this page

Two small designs come up again and again in coding interviews, because a few lines of each test invariants and amortised reasoning. A min stack answers one extra question, which of its items is the smallest, in O(1), whatever has been pushed and popped. A queue using two stacks gives first-in, first-out order with nothing but two stacks: items go onto one and come off the other, and each item moves between them at most once, so every operation is O(1) amortised. This lesson states both problems in an original setting, shows the brute force for each and why it is too slow, and names what to notice before designing.

The problems: the cheapest fare and a queue of print jobs

Problem 1. A fare-comparison site keeps the result pages a traveller has opened on a stack: opening a page pushes its fare, and the Back button pops it. Beside the Back button the site shows the cheapest fare among the pages still on the stack. Support push(fare), pop(), top() and cheapest(), each in O(1), for up to 2 × 10⁵ operations with fares from 1 to 10⁶. pop, top and cheapest on an empty stack are errors.

For example, after pushing 5400, 4200 and 6100, cheapest() is 4200; after two pops it is 5400 again.

Problem 2. Build a first-in, first-out queue of print jobs with enqueue, dequeue and front, using only two stacks and their push, pop, peek and empty operations, for up to 2 × 10⁵ operations, each O(1) amortised.

For example, enqueue A, B and C, then dequeue: A comes out. Enqueue D, then dequeue three times: B, C and D.

The brute forces: scanning and pouring

The obvious answers work and are slow. cheapest() can scan the whole stack, O(n) per call. The queue can keep its items on one stack, oldest at the bottom, and for each dequeue pour everything onto the second stack, take the top, and pour everything back, O(n) per dequeue.

Both brute forces, with their work counted Python · brute/brute.py
"""The two brute forces, with their work counted: scanning for the cheapest fare, and pouring stacks both ways."""


class ScanningMinStack:
    def __init__(self):
        self.items, self.steps = [], 0

    def push(self, fare):
        self.items.append(fare)

    def pop(self):
        return self.items.pop()

    def cheapest(self):
        self.steps += len(self.items)  # looks at every fare on the stack
        return min(self.items)


class PouringQueue:
    """Keeps the items in `stack`, oldest at the bottom; a dequeue pours everything onto `spare` and back."""

    def __init__(self):
        self.stack, self.spare, self.moves = [], [], 0

    def enqueue(self, item):
        self.stack.append(item)

    def dequeue(self):
        while self.stack:  # pour: the oldest item ends up on top of spare
            self.spare.append(self.stack.pop())
            self.moves += 1
        item = self.spare.pop()
        while self.spare:  # pour everything back
            self.stack.append(self.spare.pop())
            self.moves += 1
        return item


fares = ScanningMinStack()
for fare in [5400, 4200, 6100]:
    fares.push(fare)
print("cheapest of 5400, 4200, 6100:", fares.cheapest())
fares.pop()
fares.pop()
print("after two pops:", fares.cheapest())

q = PouringQueue()
for job in "ABC":
    q.enqueue(job)
print("dequeue order:", q.dequeue(), q.dequeue(), q.dequeue())
print()
print(f"{'n':>7} {'scans for n cheapest() after n pushes':>38} {'moves for n dequeues after n enqueues':>39}")
for n in [500, 1_000, 2_000]:
    s = ScanningMinStack()
    for i in range(n):
        s.push(i)
    for _ in range(n):
        s.cheapest()
    p = PouringQueue()
    for i in range(n):
        p.enqueue(i)
    for _ in range(n):
        p.dequeue()
    print(f"{n:>7,} {s.steps:>38,} {p.moves:>39,}")

Output

cheapest of 5400, 4200, 6100: 4200
after two pops: 5400
dequeue order: A B C

      n  scans for n cheapest() after n pushes   moves for n dequeues after n enqueues
    500                                250,000                                 250,000
  1,000                              1,000,000                               1,000,000
  2,000                              4,000,000                               4,000,000

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute.py

Both give the right answers, and both cost n² steps for n pushes followed by n queries or dequeues, as the counts show. At the stated 2 × 10⁵ operations that can mean about 10¹⁰ steps, far beyond what one second allows.

What to notice

For the fares: once a fare is on the stack, the cheapest fare at or below it never changes until it is popped, because nothing below it can change. For the print jobs: pouring one stack onto the other reverses it, which puts the oldest job on top, and once the jobs are in that order there is no reason to pour them back.

The min stack, in two designs Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Why every answer is right: the invariants
  • The two-stack queue: pour only when the outbox is empty
    • Each item moves at most once: the amortised bound, counted
  • A stack from queues, and which side pays
  • A queue that reports its minimum: two min stacks
  • The designs in five languages, with run records
  • Testing a design with random operations
  • Edge cases and common bugs
  • Practice: the two designs, with sample tests
    • A min stack and a queue made of two stacks
  • Interview questions
    • A max stack supports push, pop, top, peekMax and popMax, where popMax removes the largest item wherever it is in the stack, the newest one if several are equal. Design it, give the cost of each operation, and say why pairs of (value, maximum so far) are not enough.
    • You must build a stack on a platform that offers only queues. The workload has about 100 pushes for every pop, top is never called, and the stack holds up to 10,000 items. Which design do you choose, and what does a day of 10⁷ operations cost with each?
    • A thermostat keeps the readings of the last 30 minutes, one a second, and must show the coldest at any moment. Compare a queue of two min stacks with a monotonic deque: correctness, the worst time one reading can take, and memory.
  • Key takeaways

1 diagram · 7 runnable examples (Build and run all three, C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 18 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.