LeetCode ยท #155

Min Stack Solution

Design a stack that supports push, pop, top, and retrieving the minimum element โ€” all in O(1) time.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Medium

๐Ÿ”—

LeetCode

Problem #155

๐Ÿ—๏ธ

Pattern

Design โ€” auxiliary min stack

Design a stack class MinStack that supports: push(val) โ€” push element onto stack, pop() โ€” remove top element, top() โ€” get top element, getMin() โ€” retrieve the minimum element. Each operation must run in O(1) time.

Example
MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); // โ†’ -3 minStack.pop(); minStack.top(); // โ†’ 0 minStack.getMin(); // โ†’ -2
Constraints
โ€ข -2ยณยน โ‰ค val โ‰ค 2ยณยน - 1 โ€ข pop, top, getMin called on non-empty stacks โ€ข At most 3 ร— 10โด calls total
02
Section Two ยท Approach 1

Brute Force โ€” Scan for Min O(n)

Use a regular stack. For getMin(), scan the entire stack to find the minimum. Push, pop, and top are O(1), but getMin is O(n).

Problem: getMin scans every element. By maintaining a second stack that tracks the running minimum, we get O(1) for all operations.
OperationTime
push / pop / topO(1)
getMinO(n) โ€” scans stack
03
Section Three ยท Approach 2

Two Stacks โ€” O(1) All Operations

Maintain two stacks: a main stack for normal operations and a min stack that mirrors it, always holding the current minimum at the top. On push, push to main stack and push min(val, minStack.top) to the min stack. On pop, pop from both.

๐Ÿ’ก Mental model: Imagine two columns of sticky notes. The left column stores actual values. At the same height on the right column, you write "the smallest value in the left column from this point down." Every time you add or remove from the left, you update the right accordingly. Reading the minimum is just glancing at the top of the right column.
Algorithm
  • push(val): Push val onto main. Push min(val, minStack.peek()) onto minStack.
  • pop(): Pop from both stacks.
  • top(): Return main stack's top.
  • getMin(): Return minStack's top โ€” always the current min.
๐ŸŽฏ Why it works:
  • The min stack is synchronized with the main stack.
  • Its top always reflects the minimum of all elements currently in main.
  • When we pop from main, the next element's minimum is already stored in minStack โ€” no recomputation needed.
04
Section Four ยท Trace

Visual Walkthrough

Trace: push(-2), push(0), push(-3), getMin(), pop(), getMin():

Two Stacks โ€” main + min tracking
OPERATIONS push(-2): main: [-2] min: [-2] push(0): main: [-2, 0] min: [-2, -2] min(0, -2) = -2 push(-3): main: [-2, 0, -3] min: [-2, -2, -3] min(-3, -2) = -3 getMin(): min stack top = -3 โœ“ pop(): main: [-2, 0] min: [-2, -2] getMin(): min stack top = -2 โœ“ All operations O(1) โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Two Stacks
import java.util.Stack; class MinStack { private Stack<Integer> main = new Stack<>(); private Stack<Integer> minStack = new Stack<>(); public void push(int val) { main.push(val); int currentMin = minStack.isEmpty() ? val : Math.min(val, minStack.peek()); minStack.push(currentMin); } public void pop() { main.pop(); minStack.pop(); } public int top() { return main.peek(); } public int getMin() { return minStack.peek(); // O(1) โ€” always current min } }
Python โ€” Two Stacks
class MinStack: def __init__(self): self.main = [] self.min_stack = [] def push(self, val: int) -> None: self.main.append(val) curr_min = min(val, self.min_stack[-1]) if self.min_stack else val self.min_stack.append(curr_min) def pop(self) -> None: self.main.pop() self.min_stack.pop() def top(self) -> int: return self.main[-1] def getMin(self) -> int: return self.min_stack[-1]
06
Section Six ยท Analysis

Complexity Analysis

Approachpush/pop/topgetMinSpace
Single stack + scanO(1)O(n)O(n)
Two Stacks โ† optimal O(1) O(1) O(n) โ€” double memory
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseScenarioWhy It Matters
Single elementpush(5), getMin()Min is the only element โ†’ 5.
Duplicate minspush(1), push(1), pop()Both min entries are 1. After pop, min is still 1.
New min then poppush(3), push(1), pop()Min reverts to 3 after popping 1. MinStack handles this automatically.
Large negativespush(-2ยณยน)Integer.MIN_VALUE works โ€” no overflow in min comparison.
โš  Common Mistake: Only pushing to the min stack when the value is a new minimum. This breaks on pop โ€” if the minimum value is popped, the min stack is shorter than the main stack and falls out of sync. Always push to both stacks on every push, and pop from both on every pop.

โ† Back to Stack problems