Description
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 elementvalonto 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.
You must implement a solution with time complexity for each function.
Example 1:
Input:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
Output:
[null,null,null,null,-3,null,0,-2]
Explanation:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); // return -3
minStack.pop();
minStack.top(); // return 0
minStack.getMin(); // return -2
Constraints:
- Methods
pop,topandgetMinoperations will always be called on non-empty stacks. - At most calls will be made to
push,pop,top, andgetMin.
Approach 1: Two Stacks ( Time, Space)
Intuition
Maintain two separate stacks:
stack: Stores all inserted values in standard LIFO order.minStack: Tracks the minimum value corresponding to each state of the stack.
When pushing a value val, push val to stack, and push Math.min(val, minStack.peek()) to minStack. This ensures the top of minStack always holds the minimum element for the current stack depth.
import java.util.Stack;
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if (minStack.isEmpty()) {
minStack.push(val);
} else {
minStack.push(Math.min(val, minStack.peek()));
}
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
Complexity
- Time Complexity: for
push,pop,top, andgetMin. - Space Complexity: — Requires extra memory for
minStack.
Most Optimized Solution: Value Encoding with Single Stack ( Extra Space)
Intuition
To eliminate the second stack, maintain a single stack storing long values and a global variable min:
- Pushing a new minimum (
val < min): Instead of pushingvaldirectly, push an encoded flag value:2 * val - min. Then updatemin = val.- Because
val < min, the encoded value will always be strictly smaller thanval(the new minimum).
- Because
- Popping (
stack.peek() < min): Encountering a top value smaller thanminindicates an encoded record. Restore the previous minimum usingprev_min = 2 * min - stack.pop(). - Reading Top (
stack.peek() < min): If the top value is an encoded marker, the actual value pushed wasmin.
import java.util.Stack;
class MinStack {
private Stack<Long> stack;
private long min;
public MinStack() {
stack = new Stack<>();
}
public void push(int val) {
long value = val;
if (stack.isEmpty()) {
min = value;
stack.push(value);
} else if (value < min) {
// Encode value to store previous minimum state
stack.push(2 * value - min);
min = value;
} else {
stack.push(value);
}
}
public void pop() {
long top = stack.pop();
if (top < min) {
// Restore previous minimum
min = 2 * min - top;
}
}
public int top() {
long top = stack.peek();
if (top < min) {
return (int) min;
}
return (int) top;
}
public int getMin() {
return (int) min;
}
}
Complexity
- Time Complexity: for all operations.
- Space Complexity: extra space beyond the primary stack storage.
Easy Memory Rule
“Two Stacks store pairs of
(value, min_so_far). Single Stack encodes new minimums as2 * val - minto restore previous minimums onpop()in extra space.”
A single min variable works perfectly when pushing elements, but **it breaks completely when you pop()**.
When you pop the element that is currently the minimum, how do you know what the previous minimum was?
The Moment a Single min Variable Fails
Imagine you only keep a standard stack and a single variable min:
push(5)Stack:[5],min = 5push(3)Stack:[5, 3],min = 3push(7)Stack:[5, 3, 7],min = 3pop()Pops7.minis still3. (No problem so far!)pop()Pops3(which was your currentmin)!
- Now
3is gone from the stack. - What is the new
minfor the remaining elements[5]?
Without extra information, the only way to find out that5is the new minimum is to iterate through the entire stack and check every element—which takes time. This violates the problem requirement thatgetMin()must run in constant time.
Why Encoding (2 * val - min) is Necessary
To keep getMin() in time without scanning the stack, you must store the history of previous minimums somewhere:
- Two Stacks Approach: Stores the history in a second stack.
- Single Stack Formula (
2 * val - min): Bakes the previous minimum () directly into the number stored on the stack.
When youpop()an encoded element, the formula mathematically reconstructs in time without needing any extra space!
This formula works by creating a magic flag that solves two problems at once:
- It creates a number strictly smaller than the new minimum, signaling that a minimum change happened here.
- It mathematically encodes the previous minimum so it can be restored on
pop().
Part 1: Why stack.push(2 * value - min)?
When a new element value arrives that is smaller than the current min:
If you subtract from both sides:
Add value to both sides:
Since value becomes the new minimum (), this proves:
Key Concept: The number pushed onto the stack () is **always strictly smaller than the new
min**. This guarantees that any stack entry smaller thanminis an encoded flag, not a real raw value.
Part 2: Why if (top < min) return (int) min?
When you call top(), you check the value at the top of the stack:
- If
top >= min: The element was pushed normally (it wasn’t a new minimum), sotopis the actual value. - If
top < min: The element on top is the encoded flag ().
Since this flag was only generated whenvaluebecame the newmin, the actual value pushed WASminitself!
Therefore, iftop < min, you don’t return the dummy flag stored in the stack—you returnmin.
It restores the previous minimum using simple algebraic substitution.
The Algebraic Derivation
Recall what happens when you push(val) when val < min_old:
- The
topvalue stored in the stack was calculated as:
- The current
minvariable was then updated toval:
Now, substitutevalwithmininside the equation fortop:
To restore the previous minimum (), simply solve for :
Concrete Numerical Example
- Push
5:
- Stack:
[5] min = 5
- Push
3(New minimum!3 < 5):
- Encoded Flag =
- Push
1onto stack. - Update
min = 3. - Stack:
[5, 1]
- Call
top():
- Stack top is
1,minis3. - Check
top < min() True! - This tells us
1is just a dummy marker. The real value pushed was the minimum itself (3). - Returns
min=3.
Approach
class MinStack {
Stack<Integer> stack;
Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
val = Math.min(val, minStack.isEmpty() ? val : minStack.peek());
minStack.push(val);
}
public void pop() {
stack.pop();
minStack.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/