LeetCode Min Stack: Track Mins in O(1)
Design a stack that supports push, pop, top, and getMin in constant time. Walkthrough of the two-stack and pair-stack solutions with edge cases and interview tips.
What you'll learn
- ✓Why a single stack cannot give getMin in constant time without help
- ✓Two equivalent designs: a parallel min stack and a stack of pairs
- ✓How to handle ties on the minimum correctly during pop
- ✓Edge cases like empty stack calls and identical pushes
- ✓A short interview narrative that justifies the extra storage
Prerequisites
- •Comfort with the stack data structure
- •Basic intuition for amortized complexity
LeetCode 155, Min Stack, is a Medium design problem. It asks you to support push, pop, top, and getMin all in constant time. The catch is that a naive stack only gives you constant-time access to the top, not the minimum. We will solve it two ways, both of which run in O(1) per operation, and discuss the small bookkeeping details that interviewers probe.
The Problem
Design a stack with the following operations:
push(val)pushesvalonto the stack.pop()removes the top element.top()returns the top element.getMin()returns the minimum element currently in the stack.
All four must run in O(1) amortized time.
Brute Force
You could store values in a list and recompute the minimum on every getMin call by scanning the list.
class MinStack:
def __init__(self):
self.data = []
def push(self, val): self.data.append(val)
def pop(self): self.data.pop()
def top(self): return self.data[-1]
def getMin(self): return min(self.data)
This is correct but getMin is O(n). The problem explicitly requires constant time, so this version fails on the design intent even if it passes some test cases.
Optimal Solution
Two designs hit O(1) per operation. Pick whichever you find clearer.
Two parallel stacks
Maintain a second stack that records the running minimum. The top of the min stack is the minimum of all values currently in the main stack.
class MinStack:
def __init__(self):
self.stack = []
self.mins = []
def push(self, val: int) -> None:
self.stack.append(val)
if not self.mins or val <= self.mins[-1]:
self.mins.append(val)
def pop(self) -> None:
val = self.stack.pop()
if val == self.mins[-1]:
self.mins.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.mins[-1]
The key subtlety is the <= on push. If you push a value equal to the current minimum, you must record it again. Otherwise a later pop of that value would not also pop the min stack and getMin would lie.
Stack of pairs
Store each element together with the minimum seen so far. Every entry is self-sufficient.
class MinStack:
def __init__(self):
self.stack = []
def push(self, val: int) -> None:
cur_min = val if not self.stack else min(val, self.stack[-1][1])
self.stack.append((val, cur_min))
def pop(self) -> None:
self.stack.pop()
def top(self) -> int:
return self.stack[-1][0]
def getMin(self) -> int:
return self.stack[-1][1]
This version uses slightly more memory per element but the code is simpler and there is no equality bug to worry about.
Walk Through an Example
Perform push 3, push 5, push 2, push 2, getMin, pop, getMin, pop, getMin on the two-stack design.
- After
push 3: stack[3], mins[3]. - After
push 5: stack[3, 5], mins[3]. We do not push because 5 is larger than 3. - After
push 2: stack[3, 5, 2], mins[3, 2]. - After
push 2: stack[3, 5, 2, 2], mins[3, 2, 2]. We push because the new value ties the current min. getMinreturns 2.- After
pop: we remove 2 from both stacks. stack[3, 5, 2], mins[3, 2]. getMinreturns 2 again, which is correct because there is still a 2 in the stack.- After
pop: we remove 2 from both stacks. stack[3, 5], mins[3]. getMinreturns 3.
Without the <= rule on push, the second push 2 would not have recorded the min, and the first pop would have left mins as [3] while the stack still contained a 2. That bug is the single most common mistake on this problem.
Edge Cases
poportopon an empty stack: the problem guarantees this will not happen, but in production code you should raise an error.- Identical pushes: as shown above, the
<=comparison is essential. - Negative numbers: nothing special, the comparisons still work.
- Many duplicates of the same min: the parallel min stack can grow as large as the main stack in the worst case, which is fine and still
O(n)total space.
Complexity Analysis
- Time:
O(1)for every operation in both designs. - Space:
O(n)total, wherenis the maximum number of elements present at once. The pair design uses two integers per slot, the parallel design uses one integer per slot plus a smaller min stack.
If amortized complexity is unfamiliar, the Big O notation refresher covers the basics.
How to Explain It in an Interview
- Restate the constraint: all four operations must run in constant time.
- Argue that you need to precompute the minimum because you cannot scan during
getMin. - Pick one of the two designs and explain it in one sentence.
- Call out the equality case explicitly. Saying it before the interviewer asks is a strong signal.
- Close with the space bound and mention the alternative design as a quick contrast.
Related Problems
- LeetCode 716, Max Stack, which adds a constant-time
popMax. - LeetCode 232, Implement Queue using Stacks, another design problem in the same family.
- LeetCode 225, Implement Stack using Queues, the mirror of the above.
For background on the structure powering both versions, see stacks and queues. If you continue into harder design problems, hashing and hash maps shows the next building block you will reach for.
Wrap up
Min Stack is a clean lesson in trading a little extra memory for a major speedup. Whether you keep a parallel stack of running minima or store the minimum alongside each value, the punchline is the same: precompute what you cannot recompute fast. Nail the equality case and you have a solution that holds up under any test pattern an interviewer can throw at it.