Start learning free

LESSON 394 OF 536 · STACK

Min Stack in Python: O(1) getMin with a Second Stack

Practice O(1) Read the full explanation

A stack hands its values back in the opposite order they arrived: the last value pushed is the first one popped. Design a stack that can also say, at any moment, which value is the smallest of all the values it holds, and do every operation in constant time.

Implement the class MinStack:

  • MinStack() creates an empty stack.
  • push(val) puts the integer val on top of the stack.
  • pop() removes the value on top of the stack.
  • top() returns the value on top of the stack, without removing it.
  • getMin() returns the smallest value in the stack, without removing anything.

The input is a list of operations, applied in order to one new MinStack. The output lists what the top and getMin calls return, in order. The starter code spells these names the way your language does.

 

Example 1:

Input:
    push(-2)
    push(0)
    push(-3)
    getMin()
    pop()
    top()
    getMin()
Output: -3 0 -2
Explanation:
After the three pushes the stack holds -2, 0 and -3, with -3 on top, so the smallest value is -3.
pop() removes -3. Now 0 is on top, and the smallest value is -2 again: when the minimum leaves,
the minimum from before it arrived comes back.

Example 2:

Input:
    push(5)
    push(5)
    push(7)
    getMin()
    pop()
    pop()
    getMin()
    top()
Output: 5 5 5
Explanation: 5 is pushed twice. The two pops remove 7 and one of the 5s, but the other 5 is still in the stack, so the smallest value is still 5.

Example 3:

Input:
    push(3)
    top()
    getMin()
    push(1)
    push(2)
    top()
    getMin()
    pop()
    pop()
    getMin()
Output: 3 3 2 1 3
Explanation: The value on top (2) and the smallest value (1) can be different values. Once 2 and 1 are popped, only 3 is left, and it is the smallest again.

 

Constraints:

  • -231 <= val <= 231 - 1
  • pop, top and getMin are only called when the stack is not empty.
  • At most 2 * 105 calls in total are made to push, pop, top and getMin.

Note:

Each operation should run in O(1) time. Looking through the whole stack for its smallest value in getMin would take O(n) time per call, where n is the number of values in the stack.

STEP 1 OF 9
    Ctrl + Enter to check

    PYTHON · STACK

    Min Stack in Python, explained

    Problem overview

    A stack gives values back in reverse order: the last value pushed is the first one popped. The class MinStack must behave like a stack and also answer getMin(), the smallest value it holds right now, without removing anything. Every operation (push, pop, top, getMin) has to take constant time, and the constraints allow up to 200,000 calls, so a scan over the whole stack in getMin is too slow.

    Take the first example: push -2, 0 and -3. The stack holds [-2, 0, -3], with -3 on top, so getMin() is -3. After pop() removes -3, the top is 0 and the smallest value is -2 again. The minimum that left took its place with it, and the old one came back.

    Approach: remember the minimum at every depth

    The obvious shortcut is one extra variable holding the smallest value seen. It fails on the first example. After pop() removes -3, the variable would still say -3, and nothing tells the class what the minimum was before -3 arrived. Searching the stack for the new minimum would bring back the O(n) cost.

    The way out is a fact about stacks: a value leaves only after every value above it has left. While a value sits in the stack, the values below it cannot change. So the smallest value from the bottom up to that position can be computed once, when the value is pushed, and written down next to it. The class keeps two lists of the same length:

    • stack holds the values themselves, with the top at the end of the list.
    • minStack holds, at each depth, the smallest of the value at that depth and everything below it.

    The last entry of minStack therefore describes the whole stack as it is now, and popping removes it together with its value, which uncovers the previous minimum. For the pushes -2, 0, -3 the two lists are [-2, 0, -3] and [-2, -2, -3]. After the pop they are [-2, 0] and [-2, -2], and the minimum is -2.

    Step by step

    1. Create self.stack in __init__. An empty list is enough. A list's append and pop both work at its end, so the end of the list is the top of the stack.
    2. Create self.minStack. Another empty list, always the same length as the first one.
    3. In push, append the value to self.stack. The value goes on the ordinary stack, as on any stack.
    4. Start currentMin at val. If the stack was empty, val is its only value, so it is also the smallest.
    5. If self.minStack has entries, take the smaller of currentMin and self.minStack[-1]. That last entry is already the smallest of everything under val, so one comparison with min settles it. An empty list counts as false, so if self.minStack: runs only when there is something below.
    6. Append currentMin to self.minStack. Both lists now have the same length. A value equal to the current minimum is recorded too, so a minimum pushed twice is remembered twice.
    7. In pop, pop from both lists. They must stay the same size. Removing the last entry of minStack uncovers the minimum the stack had before that value arrived.
    8. In top, return self.stack[-1]. Index -1 is the last element of a list, and reading it removes nothing.
    9. In getMin, return self.minStack[-1]. The answer was prepared when the value was pushed.

    The solution in Python

    class MinStack:
        def __init__(self):
            self.stack = []
            self.minStack = []
    
        def push(self, val: int) -> None:
            self.stack.append(val)
            currentMin = val
            if self.minStack:
                currentMin = min(currentMin, self.minStack[-1])
            self.minStack.append(currentMin)
    
        def pop(self) -> None:
            self.stack.pop()
            self.minStack.pop()
    
        def top(self) -> int:
            return self.stack[-1]
    
        def getMin(self) -> int:
            return self.minStack[-1]
    

    A few Python details are worth knowing:

    • A list is a stack. append and pop() without an argument both work at the end of the list and take constant time on average. Popping from the front with pop(0) would shift every other element, so it is not used here.
    • Negative indexes. self.stack[-1] reads the last element without needing the length. On an empty list it raises IndexError, which is acceptable here because pop, top and getMin are only called on a non-empty stack.
    • Methods return nothing. pop in this class removes the top value and returns None. The list's own pop() returns the removed value, but the class's method does not pass it on.
    • Integers have no overflow. Python integers grow as needed, so values at the edges of the 32-bit range need no special care.

    Complexity

    Every method does a fixed number of list operations at the end of a list, so each call takes O(1) time, matching the requirement. The two lists each hold one entry per value, so the extra space is O(n) for n values in the stack. The second list is the price for constant-time minimums: it doubles the memory but keeps getMin from ever looking through the stack.

    Edge cases and common mistakes

    • Keeping one minimum variable. It breaks as soon as the minimum is popped. On the pushes -2, 0, -3 followed by a pop, a single variable still reports -3 where the answer is -2.
    • Duplicates of the minimum. Pushing 5, 5, 7 and then popping twice leaves one 5 in the stack, so getMin() must still return 5. Because minStack gets an entry for every push, including a value that only equals the current minimum, the second 5 is remembered. A version that skips equal values loses it.
    • Letting the two lists drift apart. Pop from both lists every time and push to both every time. If the lengths differ, the entry at the top of minStack no longer belongs to the value at the top of stack.
    • Top is not the minimum. With the pushes 3, 1, 2, top() returns 2 and getMin() returns 1. After two pops, only 3 is left and it is the minimum again.
    • The first push. self.minStack[-1] on an empty list would fail, which is why the if self.minStack: check comes first.

    Testing

    Try these in a file of your own, after the class definition:

    s = MinStack()
    for v in (-2, 0, -3):
        s.push(v)
    print(s.stack, s.minStack)
    print(s.getMin())
    s.pop()
    print(s.top(), s.getMin())
    

    Expected output:

    [-2, 0, -3] [-2, -2, -3]
    -3
    0 -2
    

    A second check with duplicates:

    t = MinStack()
    for v in (5, 5, 7):
        t.push(v)
    t.pop()
    t.pop()
    print(t.getMin(), t.top(), t.stack, t.minStack)
    
    5 5 [5] [5]
    
    Keep going with Python500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free