Intuition
getMin has to be O(1), but min(stack) is O(n). The trick: maintain a second stack that, at every moment, has the running minimum at its top. Push and pop in lockstep with the main stack and the top of the mins-stack is always exactly the minimum of the values currently in the main stack.
Explanation
Why we need a second stack
If we only kept one stack, finding the minimum would require scanning all elements — O(n). We could store "the current min" in a single variable, but then a pop() of the current minimum would force us to recompute by scanning. The mins-stack solves both: it remembers what the minimum would be after a pop, by stashing the historical minimum at every level.
Steps
For each operation:
push(val):- Append
valtostack. - Compute the new running min:
m = valifminsis empty, elsem = min(val, mins[-1]). Appendmtomins.
- Append
pop():- Pop the top of
stack. - Pop the top of
mins(the running min becomes whatever it was before this push).
- Pop the top of
top(): returnstack[-1].getMin(): returnmins[-1].
Walk-through
| operation | stack after | mins after | comment |
|---|---|---|---|
push(-2) | [-2] | [-2] | first push, min = -2 |
push(0) | [-2, 0] | [-2, -2] | new min = min(0, -2) = -2 |
push(-3) | [-2, 0, -3] | [-2, -2, -3] | new min = min(-3, -2) = -3 |
getMin() → -3 | unchanged | unchanged | top of mins |
pop() | [-2, 0] | [-2, -2] | min reverts to -2 automatically |
top() → 0 | unchanged | unchanged | top of main stack |
getMin() → -2 | unchanged | unchanged |
Why the mins-stack stays correct
By induction: after each push, mins[-1] equals min of all values currently in stack (because we computed it as min(new value, previous min)). After each pop, both stacks shrink in lockstep, so mins[-1] once again equals the min of the remaining values.
Edge cases
- Empty stack: the problem says ops only happen on non-empty stacks for
topandgetMin, so we don't need to guard. - Duplicates:
push(2); push(2)producesmins = [2, 2]. Theminsemantics still work — popping one2leaves the other on top. - All identical values:
minsis just a copy ofstack. Still O(n) extra, but correct.
Complexity
- Time:
O(1)per operation — just stack push/pop. - Space:
O(n)— two stacks, each holds up tonitems.
Implementation
import java.util.*;
class MinStack {
private Deque<Integer> stack;
private Deque<Integer> mins;
public MinStack() {
stack = new ArrayDeque<>();
mins = new ArrayDeque<>();
}
public void push(int val) {
stack.push(val);
mins.push(mins.isEmpty() ? val : Math.min(val, mins.peek()));
}
public void pop() {
stack.pop();
mins.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return mins.peek();
}
}