Problem statement
Brute force
We can use a Python list to act as a stack such that the push, pop, and top operations
all require $O(1)$ time. The getMin operation can be implemented through a linear
search, but this requires $O(n)$ time where $n$ is the length of the stack.
We can do better.
Optimized solution
To bring the time complexity of the getMin operation down to $O(1)$, we use two
stacks: one stack to store the actual values and another stack to store the cumulative
minimums of the first stack.
For example, consider the following input sequence of operations and the resulting output (if any) and the values contained in both stacks after the operation is completed:
push(1):output: None,stack: [1], cum_min_stack: [1].push(2):output: None,stack: [1, 2], cum_min_stack: [1, 1].push(0):output: None,stack: [1, 2, 0], cum_min_stack: [1, 1, 0].push(1):output: None,stack: [1, 2, 0, 3], cum_min_stack: [1, 1, 0, 0].getMin():output: 0,stack: [1, 2, 0, 3], cum_min_stack: [1, 1, 0, 0].pop():output: None,stack: [1, 2, 0], cum_min_stack: [1, 1, 0].pop():output: None,stack: [1, 2], cum_min_stack: [1, 1].top():output: 2,stack: [1, 2], cum_min_stack: [1, 1].getMin():output: 1,stack: [1, 2], cum_min_stack: [1, 1].
We see that stack contains the actual values while cum_min_stack[i] = min(stack[0:i]),
and so we can obtain the minimum value in the stack as the top of the cum_min_stack.
Because getMin() would involve checking the top of cum_min_stack, it would involve
only $O(1)$ time, as desired.
We implement the MinStack class in Python as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MinStack:
def __init__(self):
self.stack = []
self.cum_min_stack = []
def push(self, val: int) -> None:
self.stack.append(val)
if len(self.cum_min_stack) == 0:
self.cum_min_stack.append(val)
else:
self.cum_min_stack.append(min(val, self.cum_min_stack[-1]))
def pop(self) -> None:
self.stack.pop()
self.cum_min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.cum_min_stack[-1]