2Brute2Best
πŸ”₯0

Min Stack

MediumΒ·LeetCode #155Basic Anchor
LeetCode
30-Second Mental ModelStack simulation / design (stack of (value, min so far))
Complexity:O(N)β†’O(1)
Key Insight

Every entry on the stack is a pair (value, minSoFar), where minSoFar is the smallest value from the bottom of the stack up to and including this entry. push computes it from the entry below. getMin just reads the top entry's minSoFar. pop needs no extra work.

Core Validity Invariant

Every entry's minSoFar is the smallest value from the bottom of the stack up to that entry. Because entries below the top never change, this stays true for as long as the entry is on the stack.

01

Problem

Build a class MinStack: a normal stack that can also tell you its smallest value at any moment.

  • MinStack() creates an empty stack.
  • void push(int val) puts val on top.
  • void pop() removes the top value.
  • int top() returns the top value.
  • int getMin() returns the smallest value currently on the stack.

Every one of these must run in O(1) time.

Input

A sequence of calls to push, pop, top and getMin.

Output

The return value of each top and getMin call. push and pop return nothing.

Example 1
Input: push(-2), push(0), push(-3), getMin(), pop(), top(), getMin()
Output: -3, 0, -2 (the results of getMin, top, getMin)

With -2, 0, -3 on the stack the smallest is -3. After popping -3 the top is 0, and the smallest of what is left (-2, 0) is -2.

Constraints
  • β€’ βˆ’2^31 ≀ val ≀ 2^31 βˆ’ 1
  • β€’ pop, top and getMin are only called when the stack is not empty
  • β€’ At most 3 Β· 10^4 calls in total
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck β€” each hint is counted.

  1. 1.

    If I keep one variable minVal, what goes wrong when the current minimum is popped?

  2. 2.

    When a value is pushed, which values can it be compared with? Which values are below it, and do they ever change while it is on the stack?

  3. 3.

    For a given entry, is "the smallest value from the bottom up to here" fixed, or can later pushes and pops change it?

  4. 4.

    What happens if the same minimum value is pushed twice and then popped once?

  5. 5.

    Which is the trade-off: extra memory for every entry, or extra memory only for some entries?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForcePlain vector stack; getMin scans every valueO(N)O(N)
BetterMain stack + a stack of mins pushed only when val <= current minO(1)O(N)
OptimalEach entry stores its value and the min of everything up to itO(1)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • β€œpush / pop / top”Last in, first out: changes only ever happen at the top.
  • β€œgetMin in O(1)”We cannot scan, so the answer must be stored somewhere and ready to read.
  • β€œafter a pop, the min may go back to an older value”Old answers must be remembered in the same last-in, first-out order β†’ store them in the stack itself.
Changes only at the top+query about the whole stack+answer fixed per height→Stack simulation / design (stack of (value, min so far))

This is the anchor for the sub-pattern: the stack is not used to match or cancel anything, it is the thing being simulated, and each entry carries extra info computed from the entry below. Simplify Path and Asteroid Collision also simulate a process with a stack, but there new items actively remove old ones (.. pops a folder, a left-mover destroys right-movers). Here pops only come from the caller.

10

Don't make this mistake

Keeping a single minVal variable
βœ— void push(int val) { st.push(val); minVal = min(minVal, val); } void pop() { st.pop(); } // what is the min now?
βœ“ st.push({val, st.empty() ? val : min(val, st.top().second)});

Pattern-confusion trap: this is not "running minimum of an array". An array only grows, but a stack also shrinks. When the current min is popped, one variable cannot tell you what the min was before it was pushed. You need to remember the min at every height.

Using < instead of <= in the two-stack version
βœ— if (mins.empty() || val < mins.top()) mins.push(val);
βœ“ if (mins.empty() || val <= mins.top()) mins.push(val);

Push 3, then 3 again. With < only one 3 is in mins. Pop the second 3: it equals mins.top(), so mins is popped, and getMin now returns something bigger than 3 even though a 3 is still on the stack.

Forgetting to pop the mins stack
βœ— void pop() { st.pop(); }
βœ“ void pop() { if (st.top() == mins.top()) mins.pop(); st.pop(); }

If the popped value was the current min, mins still reports it, so getMin returns a value that is no longer on the stack. Check before popping st, while you can still read the value.

Using the top value of the stack below instead of its stored min
βœ— int curMin = min(val, st.top().first);
βœ“ int curMin = min(val, st.top().second);

st.top().first is just the last value pushed, not the smallest. Push 1, 5, 3: with .first the entry for 3 would store min(3, 5) = 3, but the real min is 1.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Stack translate into production architectures and low-level memory mechanics.