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.

Coding Interview Patterns  Module 8 – Stacks and monotonic stacks

Stacks that know their minimum

Design a stack whose push, pop, top and minimum each take O(1): an original fuel-log problem, the scan that fails at 10⁵ calls, and the key idea.

  • Beginner
  • 20 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7 and Java temurin-25.0.4+101.0.LTS
  • By MySmartCoPilot
Free intro, Pro: 5 of 20 minutes free
Free: The problem: the cheapest fill-up of a trip · What to notice. With a pass: the other 10 sections and 3 interview answers, about 15 minutes.

What you will learn

  • Support push, pop, top and the minimum in O(1) time, and prove the design correct
  • Store pairs or a second stack of minimums, and compare their memory with measured numbers
  • Avoid the duplicate-minimum and boxed-integer slips, and the overflow of the encoded single stack
  • Build a queue with an amortised O(1) minimum from two stacks

Before you start

What you’ll learn · Before you start

What you will learn

  • Support push, pop, top and the minimum in O(1) time, and prove the design correct
  • Store pairs or a second stack of minimums, and compare their memory with measured numbers
  • Avoid the duplicate-minimum and boxed-integer slips, and the overflow of the encoded single stack
  • Build a queue with an amortised O(1) minimum from two stacks

Before you start

On this page

A min stack: A stack that can also report its smallest value at any moment, with push, pop, top and the minimum each in O(1) time, because each level remembers the minimum at or below it. is a stack that can also report its smallest value at any moment, with push, pop, top and the minimum each in O(1) time. It is needed wherever the newest item can be taken back while a running best must stay right: undo in a ledger, backtracking with a best-so-far, the cheapest fill-up on a road trip. Scanning the whole stack for the minimum costs O(n) per question, which is far too slow at 10⁵ operations. The fix rests on one observation: a stack only ever removes its newest item, so the minimum of everything below an item cannot change while that item is there, and each level can remember it. Below are an original version of the problem, a measured brute force and the signal that leads to the fix.

The problem: the cheapest fill-up of a trip

The problem

A fuel-log app records the price per litre of each fill-up on a road trip, as a whole number of the smallest currency unit. The driver may undo the latest fill-up, and the app must always show the cheapest price among the fill-ups still in the log. Write a class with fill(price), undo(), latest() and cheapest(), each in O(1) time. There are at most 2 × 10⁵ operations, every price is from 1 to 10⁹, and undo, latest and cheapest are called only when the log is not empty.

Two examples, run by every version of the code below:

  1. Fill 9472, 9310 and 9655: the cheapest is 9310. After two undos only 9472 is left, so the cheapest and the latest are both 9472.
  2. Fill 9310 twice and undo once: the cheapest is still 9310, because one fill-up at that price remains.

The brute force: scan the log

Keep the prices on a plain stack and let cheapest() look at every one of them. Filling, undoing and reading the latest price stay O(1), but each cheapest() costs O(n), so 10⁵ fill-ups followed by 10⁵ calls scan 10¹⁰ prices.

A plain stack that scans for the cheapest price Python · brute/scan_log.py
"""The brute force: a plain stack of fill-ups that scans every price for cheapest()."""
import random
import time


class ScanLog:
    def __init__(self):
        self.prices = []

    def fill(self, price):
        self.prices.append(price)

    def undo(self):
        self.prices.pop()

    def latest(self):
        return self.prices[-1]

    def cheapest(self):
        return min(self.prices)  # looks at every fill-up still in the log: O(n)


log = ScanLog()
for price in (9472, 9310, 9655):
    log.fill(price)
print("example 1:", log.cheapest(), end=" ")
log.undo()
log.undo()
print(log.cheapest(), log.latest())

log = ScanLog()
log.fill(9310)
log.fill(9310)
log.undo()
print("example 2:", log.cheapest())

rng = random.Random(11)
per_price = 0.0
for n in (1_000, 2_000, 4_000):
    log = ScanLog()
    for _ in range(n):
        log.fill(rng.randrange(8_000, 11_000))
    best = float("inf")
    for _ in range(3):  # the fastest of three runs: the least disturbed by other programs
        start = time.perf_counter()
        for _ in range(n):
            log.cheapest()
        best = min(best, time.perf_counter() - start)
    per_price = best / (n * n)
    print(f"n = {n:,}: {n:,} fill-ups, then {n:,} cheapest() calls scan {n * n:,} prices in {best * 1000:,.0f} ms")

# Every call scans all n prices, so the work grows with n squared.
print(f"n = 100,000: {100_000 * 100_000:,} prices, about {100_000 * 100_000 * per_price / 60:,.0f} minutes at that speed")

Output

example 1: 9310 9472 9472
example 2: 9310
n = 1,000: 1,000 fill-ups, then 1,000 cheapest() calls scan 1,000,000 prices in 12 ms
n = 2,000: 2,000 fill-ups, then 2,000 cheapest() calls scan 4,000,000 prices in 165 ms
n = 4,000: 4,000 fill-ups, then 4,000 cheapest() calls scan 16,000,000 prices in 604 ms
n = 100,000: 10,000,000,000 prices, about 6 minutes at that speed

This output changes from run to run: the times depend on the computer and on what else it is doing

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

Each doubling of n scans four times as many prices. At the limit, the recorded run puts 10¹⁰ prices at minutes of work, where a time limit allows about a second.

Checkpoint

Check: What scanning the log costs

  1. One question The recorded run scanned 16,000,000 prices for 4,000 fill-ups and 4,000 cheapest() calls. How many for 100,000 of each?

    Choose one answer.

    Show the answer: One question

    Answer: 10,000,000,000

    The work is n calls of n prices each, n². From 4,000 to 100,000 is 25 times the size, so 625 times the work: 16,000,000 × 625 = 10,000,000,000.

What to notice

The cheapest price changes only when a fill-up brings a lower price, or when an undo removes the fill-up that set it, and then it returns to exactly what it was before that fill-up. So the cheapest price belongs to each level of the log: the minimum of that level and everything below it, which stays the same while the level is there.

The fuel log after fill-ups 9472, 9310 and 9655: each level shows its price and the cheapest at or below it; after two undos 9472 is left.Newest on topAfter two undos:9472 is left, and itslevel already says 94729655cheapest at or below: 93109310cheapest at or below: 93109472cheapest at or below: 9472undo, undo

Each level knows the cheapest price at or below it

Text description of the diagram

The diagram shows the fuel log after three fill-ups, with the newest one at the top, drawn with a thick border.

  1. The top level holds the price 9655. The cheapest price at or below it is 9310.
  2. The middle level holds 9310. The cheapest price at or below it is 9310, its own.
  3. The bottom level holds 9472, the first fill-up. The cheapest price at or below it is 9472.

An arrow labelled "undo, undo" leads to the state after two undos: only 9472 is left, and its level already says that the cheapest price is 9472. The cheapest price at or below a level never changes while that level is in the log, because a stack only ever removes its newest level.

Three designs build on this observation:

  • Pairs: each level stores its price together with the cheapest price at or below it.
  • A second stack of minimums: it takes a price only when the price is at most the current cheapest.
  • One encoded stack: it stores a new minimum as twice the price minus the old minimum, with no second stack.

Checkpoint

Check: The minimum each level can remember

  1. One question The log holds 9472, 9310 and 9655, the newest on top. What is the cheapest price at or below the top level?

    Choose one answer.

    Show the answer: One question

    Answer: 9310

    The cheapest at or below a level is the minimum of its price and every price below it: min(9655, 9310, 9472) = 9310. It stays the same while that level is in the log.

These questions cover the scan and the signal, weakest idea first.

Practice that adapts to you

Practise this lesson

6 questions about 9 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 In the brute force, which call is slow?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: cheapest(), because it looks at every fill-up still in the log

    Filling, undoing and reading the latest price stay O(1) on a plain stack; only cheapest() scans, O(n) a call.

  2. Question 2 of 6 100,000 fill-ups are followed by 100,000 cheapest() calls. How many prices does the brute force scan in all?

    Type a number.

    A hint

    Each call scans every price in the log.

    Show the answer: Question 2 of 6

    Answer: 10000000000 prices

    Each of the 100,000 calls scans all 100,000 prices: 10¹⁰, minutes of work where a time limit allows about a second.

  3. Question 3 of 6 Would keeping the log's prices in sorted order fix the brute force?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: No, because each fill-up must then be inserted in place, and undo still has to find the latest fill-up

    A sorted list answers cheapest() at once but makes fill() an insertion and undo() a search for the fill-up that came last. The fix keeps the stack's order and stores each level's minimum instead.

  4. Question 4 of 6 When can the cheapest price of the log change?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: When a fill-up brings a lower price, or an undo removes the fill-up that set it

    A stack only removes its newest item, so the cheapest returns to what it was before the removed fill-up: each level can remember the minimum at or below it.

  5. Question 5 of 6 The log is filled with 9500, 9400 and 9600, and then one fill-up is undone. What is the cheapest price?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: 9400

    After the undo the log holds 9500 and 9400, and the top level has remembered min(9400, 9500) = 9400 since it was filled.

  6. Question 6 of 6 A different app lets the driver delete any fill-up, not only the latest. Does "each level remembers the minimum at or below it" still work?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: No, because deleting a level from the middle changes the minimum of every level above it

    The minimum at or below a level stays the same only while nothing below it can go. A delete from the middle breaks that, which is why the trick belongs to stacks.

Pairs: every level remembers its cheapest Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A second stack keeps only the minimums, repeats included
    • The < slip, reproduced: a repeated minimum lost
  • Memory, measured: pairs against a second stack
  • The code in 14 languages
    • The Java slip, reproduced: comparing boxed integers with ==
  • One stack and one variable: the encoded trick, and where 32 bits break it
  • A queue that knows its minimum, from two stacks
  • Edge cases, common bugs and variants
    • Variants, with answers
  • Practise: a min stack with sample tests
    • Build a stack that knows its minimum
  • Interview questions
    • A Java min stack keeps a second ArrayDeque<Integer> of minimums. It passes every test with small numbers, but in production, with prices in the thousands, cheapest() sometimes returns a price that was undone. Find the likely bug, explain why the small tests miss it, and fix it.
    • A min stack must hold up to 10⁸ 32-bit readings on a device with 1 GB of memory. Compare pairs, a second stack of minimums and the encoded single stack: what does each cost in the worst case, and which do you ship?
    • Prices arrive one per second, and after each one a dashboard shows the lowest price of the last 600 seconds. Design it with O(1) amortised work per price, prove the bound, and say what the slowest single update costs.
  • Key takeaways

2 diagrams · 6 runnable examples (Java, Python) · 1 exercise with sample tests · 3 interview answers · about 38 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).

Terms in this lesson

min stack
A stack that can also report its smallest value at any moment, with push, pop, top and the minimum each in O(1) time, because each level remembers the minimum at or below it.

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.