Skip to content

Min Stack

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

Implement the MinStack class:

  • MinStack() Initializes the stack object.
  • void push(int val) Pushes the element val onto the stack.
  • void pop() Removes the element on the top of the stack.
  • int top() Gets the top element of the stack.
  • int getMin() Retrieves the minimum element in the stack.

Each operation must run in O(1) time.

OperationStackMinNotes
push(-2)[-2]-2Initialize with minimum as -2
push(0)[-2, 0]-2New min is still -2
push(-3)[-2, 0, -3]-3New min is -3
getMin()[-2, 0, -3]-3Return -3 ✓
pop()[-2, 0]-2Remove -3, min reverts to -2
top()[-2, 0]Return 0 ✓
getMin()[-2, 0]-2Return -2 ✓
  • -2^31 <= val <= 2^31 - 1
  • pop, top, and getMin operations will always be called on non-empty stacks.
  • At most 3 × 10^4 calls will be made to push, pop, top, and getMin.
ApproachTimeSpaceAdditional MemoryBest When
Dual StackO(1) all opsO(n)O(n) for min stackClearer logic, easier to debug
Single Stack with PairsO(1) all opsO(n)O(n) same stackMemory-conscious, stores both in one place
  • Pick Dual Stack if you want the simplest mental model: one stack for values, one for mins.
  • Pick Single Stack with Pairs if you want to understand space optimization and tuple/pair handling.
Clearer Logic
Dual Stack
O(1) all ops · O(n) space
Space Optimized
Single Stack with Pairs
O(1) all ops · O(n) space
★ Recommended

Maintain two stacks:

  1. Main stack: stores all pushed values.
  2. Min stack: stores the minimum value at each level (only pushes/pops in sync with main stack).

Whenever we push a value to the main stack, we push min(val, current_min) to the min stack. When we pop from the main stack, we pop from the min stack too. This way, getMin() is always O(1) by peeking the top of the min stack.

⏱ Time O(1) All operations constant 💾 Space O(n) Two stacks of size n

Sequence of operations: push(-2), push(0), push(-3), getMin(), pop(), top(), getMin()

StepOperationMain StackMin StackResultAction
1push(-2)[-2][-2]First element: min is -2 itself
2push(0)[-2, 0][-2, -2]min(-2, 0) = -2, so push -2
3push(-3)[-2, 0, -3][-2, -2, -3]min(-2, -3) = -3, so push -3
4getMin()[-2, 0, -3][-2, -2, -3]-3Peek top of min stack → -3 ✓
5pop()[-2, 0][-2, -2]Remove from both stacks
6top()[-2, 0][-2, -2]0Peek main stack → 0 ✓
7getMin()[-2, 0][-2, -2]-2Peek min stack → -2 ✓

Animated walkthrough

How the dual stack solution tracks the minimum

Watch both stacks grow and shrink as you push, pop, and query. The min stack always shows the minimum at each level.

Step 1 of 8
Waiting to begin...
Array state
Memory / lookup state

class MinStack:
minStack = {} // main stack for all values
mainStack = {} // separate stack for minimums
function push(val):
mainStack.push(val)
if minStack is empty:
minStack.push(val)
else:
minStack.push(min(val, minStack.top()))
function pop():
mainStack.pop()
minStack.pop()
function top():
return mainStack.top()
function getMin():
return minStack.top()
min_stack_dual.py
class MinStack:
"""Min Stack using two stacks (dual stack approach).
Maintains a main stack for all values and a separate min stack
that tracks the minimum value at each level.
Time: O(1) per operation
Space: O(n)
"""
def __init__(self):
self.main_stack = []
self.min_stack = []
def push(self, val: int) -> None:
self.main_stack.append(val)
# Push to min_stack the minimum of val and current min
if not self.min_stack:
self.min_stack.append(val)
else:
self.min_stack.append(min(val, self.min_stack[-1]))
def pop(self) -> None:
self.main_stack.pop()
self.min_stack.pop()
def top(self) -> int:
return self.main_stack[-1]
def getMin(self) -> int:
return self.min_stack[-1]
# Example usage
stack = MinStack()
stack.push(-2)
stack.push(0)
stack.push(-3)
print(f"Min: {stack.getMin()}") # -3
stack.pop()
print(f"Top: {stack.top()}") # 0
print(f"Min: {stack.getMin()}") # -2
🎯 Interview Favourite

Instead of maintaining two separate stacks, store one element per level as a pair (value, min_at_this_level).

Each pair holds:

  • value: the actual value being pushed
  • min_at_this_level: the minimum from the bottom of the stack up to and including this element

When you push a new value, compute its minimum as min(val, previous_min) and store the pair. This way, the entire history of minimums is embedded in the stack itself.

⏱ Time O(1) All operations constant 💾 Space O(n) Single stack with pairs

Sequence of operations: push(-2), push(0), push(-3), getMin(), pop(), top(), getMin()

StepOperationStackTop PairMinAction
1push(-2)[(-2, -2)](-2, -2)-2First: value=min=-2
2push(0)[(-2, -2), (0, -2)](0, -2)-2min(0, -2) = -2
3push(-3)[(-2, -2), (0, -2), (-3, -3)](-3, -3)-3min(-3, -2) = -3
4getMin()[(-2, -2), (0, -2), (-3, -3)](-3, -3)-3Return second element of top pair
5pop()[(-2, -2), (0, -2)](0, -2)-2Remove top pair
6top()[(-2, -2), (0, -2)](0, -2)Return 0 (first element of top pair)
7getMin()[(-2, -2), (0, -2)](0, -2)-2Return second element of top pair
class MinStack:
stack = [] // stores (value, min_at_this_level) pairs
function push(val):
if stack is empty:
stack.push((val, val))
else:
currentMin = min(val, stack.top().min)
stack.push((val, currentMin))
function pop():
stack.pop()
function top():
return stack.top().value
function getMin():
return stack.top().min
min_stack_pair.py
class MinStack:
"""Min Stack using single stack with (value, min) pairs.
Each element in the stack is a tuple of (value, min_at_this_level).
The min_at_this_level is the minimum value from the bottom up to
and including the current element.
Time: O(1) per operation
Space: O(n)
"""
def __init__(self):
self.stack = []
def push(self, val: int) -> None:
if not self.stack:
# First element: value is the minimum
self.stack.append((val, val))
else:
# Store both value and the minimum up to this point
current_min = min(val, self.stack[-1][1])
self.stack.append((val, current_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]
# Example usage
stack = MinStack()
stack.push(-2)
stack.push(0)
stack.push(-3)
print(f"Min: {stack.getMin()}") # -3
stack.pop()
print(f"Top: {stack.top()}") # 0
print(f"Min: {stack.getMin()}") # -2
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
min_stack_space_optimized.py
"""
Solution for Min Stack
"""
def solve():
"""Implementation for approach 3 of Min Stack"""
pass
if __name__ == "__main__":
print("Solution ready")