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.You must implement a solution with O(1) 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:
-231 <= val <= 231 - 1pop, top and getMin operations will always be called on non-empty stacks.3 * 104 calls will be made to push, pop, top, and getMin.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach to this problem involves checking every single number that has been added to our special stack to find the minimum value. We do this every time we need to know what the minimum is.
Here's how the algorithm would work step-by-step:
class MinStackBruteForce:
def __init__(self):
self.stack_elements = []
def push(self, value_to_add):
self.stack_elements.append(value_to_add)
def pop(self):
# Ensure we don't pop from an empty stack
if self.stack_elements:
self.stack_elements.pop()
def top(self):
# Return the top element without removing it
if self.stack_elements:
return self.stack_elements[-1]
return None
def get_min(self):
# We need to iterate through all elements to find the minimum
if not self.stack_elements:
return None
minimum_value_found = self.stack_elements[0]
# Check every element to find the absolute smallest
for element_in_stack in self.stack_elements:
if element_in_stack < minimum_value_found:
minimum_value_found = element_in_stack
return minimum_value_foundThe goal is to create a special kind of stack that can quickly tell you the smallest number currently inside it. The trick is to keep track of not just the numbers themselves, but also what the smallest number was when each number was added.
Here's how the algorithm would work step-by-step:
class MinStack:
def __init__(self):
self.main_stack_elements = []
self.minimum_tracker_stack = []
def push(self, value_to_push):
self.main_stack_elements.append(value_to_push)
# When pushing, we need to record the minimum up to this point.
if not self.minimum_tracker_stack:
self.minimum_tracker_stack.append(value_to_push)
else:
# We store the smaller of the new value or the current minimum.
current_minimum = self.minimum_tracker_stack[-1]
self.minimum_tracker_stack.append(min(value_to_push, current_minimum))
def pop(self):
if self.main_stack_elements:
self.minimum_tracker_stack.pop()
# Removing an element also requires removing its corresponding minimum tracking.
return self.main_stack_elements.pop()
return None
def top(self):
if self.main_stack_elements:
return self.main_stack_elements[-1]
return None
def getMin(self):
# The minimum is always at the top of the separate minimum tracking stack.
if self.minimum_tracker_stack:
return self.minimum_tracker_stack[-1]
return None| Case | How to Handle |
|---|---|
| Calling pop, top, or getMin on an empty stack | The implementation should raise an error or return a sentinel value (e.g., None or an exception) to indicate an invalid operation. |
| Pushing a new minimum value | The solution must update the stored minimum to the new value, ensuring getMin() always reflects the current minimum. |
| Popping the current minimum value | The solution needs to correctly track the previous minimum value to maintain accurate getMin() results. |
| Stack with only one element | Pushing and then immediately popping or getting the minimum should function correctly. |
| Stack with all identical elements | The minimum should remain constant, and pop operations should correctly update to the same previous minimum. |
| Stack with negative numbers and zero | The comparison logic for minimums must handle negative values and zero correctly. |
| Extremely large number of pushes leading to memory limits | The solution's space complexity should be considered; using an auxiliary stack for minimums is generally efficient. |
| Pushing values that approach integer limits | Ensure the chosen data type for storing stack elements and minimums can accommodate the full range of potential input values without overflow. |