Sale: Get 60% Off on all Pro Plans. Buy Now

Min Stack

Easy Monotonic Stack

Problem

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

  • Methods push, pop, top, and getMin must all operate in O(1) time.
  • pop, top, and getMin will always be called on non-empty stacks.

Example

Input: push(2), push(0), push(3), push(0), getMin(), pop(), getMin(), pop(), getMin(), pop(), getMin()
Output: [null, null, null, null, 0, null, 0, null, 0, null, 2]

The stack evolves as follows: push 2 → stack=[2], min_stack=[2]; push 0 → stack=[2,0], min_stack=[2,0]; push 3 → stack=[2,0,3], min_stack=[2,0,0]; push 0 → stack=[2,0,3,0], min_stack=[2,0,0,0]. getMin() returns 0 (the top of min_stack). pop() removes 0 → stack=[2,0,3], min_stack=[2,0,0]. getMin() still returns 0. Subsequent pops and getMin() calls update the stacks accordingly, always returning the current minimum in O(1) time.

Approach

Straightforward Solution

A naive approach is to scan the entire stack to find the minimum after each push or pop, resulting in O(n) time per getMin call, which is inefficient for large stacks.

Core Observation

A stack supports last-in-first-out operations, but retrieving the minimum element in constant time requires additional state tracking. The minimum element can change with each push or pop, so a secondary structure must track the minimums efficiently.

Path to Optimal

Preview

The key insight is to maintain a parallel stack (min_stack) that tracks the minimum element at each level of the main stack. When pushing a new value, compare it with the current minimum and push the smaller one onto min_stack…

Full step-by-step walkthrough on Pro

Optimal Approach

Preview

Use two stacks: one for all values (stack) and one for the current minimums (min_stack). On push, append the value to stack and the minimum of the new value and the current min to min_stack…

Full step-by-step walkthrough on Pro

Want the full reasoning chain?

Unlock the complete walkthrough, line-by-line analysis, and recall drill.

Unlock Pro

Time

O(1)

All operations (push, pop, top, getMin) perform a fixed number of stack operations (append, pop, or peek) each taking constant time.

Space

O(n)

Two stacks are maintained, each potentially storing all elements in the worst case, resulting in O(n) auxiliary space proportional to the number of pushed elements.

Pattern Spotlight

Stack (Auxiliary Stack for State Tracking)

Maintain an auxiliary stack that mirrors the main stack's operations but stores the minimum value at each state, enabling constant-time retrieval of the minimum element by always keeping the current minimum at the top.

Solution

Python
1class MinStack:
2 def __init__(self):
3 self.stack = []
4 self.min_stack = []
5
6 def push(self, val: int) -> None:
7 self.stack.append(val)
8 val = min(val, self.min_stack[-1] if self.min_stack else val)
9 self.min_stack.append(val)
10
11 def pop(self) -> None:
12 self.stack.pop()
13 self.min_stack.pop()
14
15 def top(self) -> int:
16 return self.stack[-1]
17
18 def getMin(self) -> int:
19 return self.min_stack[-1]

Step-by-Step Solution

1

Maintain Parallel Stacks to Track Values and Minimums on Push

6def push(self, val: int) -> None:
7 self.stack.append(val)
8 val = min(val, self.min_stack[-1] if self.min_stack else val)
9 self.min_stack.append(val)

Objective

To push the new value onto the main stack and update the min_stack with the current minimum after the push.

Key Insight

By pushing the minimum of the new value and the current minimum onto min_stack, the algorithm preserves the minimum element at every stack depth. This approach ensures that the top of min_stack always reflects the minimum of all elements below or equal to the current top of the main stack, enabling constant-time minimum retrieval.

Interview Quick-Check

Core Logic

Push the new value onto the main stack and push the minimum of the new value and the current minimum onto min_stack to maintain synchronized minimum tracking.

State & Boundaries

When min_stack is empty, the new value is the minimum by default.

Common Pitfalls & Bugs

Failing to update min_stack correctly leads to incorrect minimum values and breaks the O(1) getMin guarantee.

2

Synchronize Stack and Min_Stack on Pop to Maintain Consistency

To remove the top elements from both the main stack and min_stack, ensuring both remain aligned.

3

Retrieve the Top Element and Current Minimum in Constant Time

To return the top element of the main stack and the current minimum from min_stack efficiently.

2 more steps with full analysis available on Pro.

Line Analysis

This solution has 3 Critical lines interviewers watch for.

Line 8 Critical
val = min(val, self.min_stack[-1] if self.min_stack else val)

Calculate the new minimum by comparing the pushed value with the current minimum.

This line ensures the min_stack always holds the minimum value up to the current stack depth by choosing the smaller between the new value and the existing minimum.

Line 13 Critical
self.min_stack.pop()

Pop the top element from the min_stack.

Removing the corresponding minimum value keeps min_stack aligned with the main stack, preserving correct minimum tracking.

Line 19 Critical
return self.min_stack[-1]

Return the top element of the min_stack, representing the current minimum.

Because min_stack mirrors the main stack's state with minimum values, its top element is always the current minimum, enabling O(1) retrieval.

Full line-by-line criticality + rationale for all 13 lines available on Pro.

Test Your Understanding

Why is it necessary to maintain a separate min_stack alongside the main stack?

See the answer with Pro.

Related Problems

Monotonic Stack pattern

Don't just read it. Drill it.

Reconstruct Min Stack from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the Min Stack drill

or drill a free problem