Start learning free

LESSON 399 OF 541 · STACK

Min Stack in Swift: O(1) push, pop, top and getMin

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

    SWIFT · STACK

    Min Stack in Swift, explained

    Problem overview

    A stack gives values back in reverse order: the last value pushed is the first one popped. The task is to build a MinStack class that also answers "what is the smallest value in the stack right now?" through getMin(), and to make every operation (push, pop, top and getMin) run in constant time.

    The calls are applied in order to one new MinStack, and the results of top and getMin are listed. Take the first example: push -2, push 0, push -3. The stack holds -2, 0, -3 with -3 on top, so getMin() is -3. After pop() the stack is -2, 0: top() is 0 and getMin() is -2 again. The output is -3 0 -2.

    The lesson's tests only call pop, top and getMin on a non-empty stack, so the code doesn't need to handle an empty one.

    Approach: remember the minimum next to each value

    The obvious way to answer getMin is to look through the whole array, which costs O(n) per call. With up to 200,000 calls, that is too slow. Keeping a single min variable fails differently: when the minimum is popped, the variable would have to be recomputed, and that is the same O(n) scan.

    The way out comes from one observation. A value leaves the stack only after every value above it has left. While it is in the stack, the values below it can't change. So the minimum of "this value and everything below it" is known at the moment of the push and stays correct for as long as that value exists. Write it down once, next to the value.

    The class therefore keeps two arrays of the same length:

    • stack holds the values, with the top of the stack at the end of the array;
    • minStack holds, at each depth, the smallest value from the bottom up to that depth.

    Pushing 3, 1, 2 gives stack = [3, 1, 2] and minStack = [3, 1, 1]. The last entry of minStack always describes the whole stack, so getMin only reads it. Popping removes the last entry of both arrays, which uncovers the minimum the stack had before that value arrived. In the first example, popping -3 leaves minStack = [-2, -2], and the answer -2 is already there.

    Step by step

    1. Declare stack as a var of type [Int], set to an empty array. It has to be a var because the methods change it, and appending to or removing from the end of an array is cheap.
    2. Declare minStack the same way, for the running minimums.
    3. In push, append the value to stack first, as on any stack.
    4. Start currentMin at val. If the stack was empty, val is its only value and so its minimum. It is a var because the next step may lower it.
    5. If minStack isn't empty, compare currentMin with the top of minStack and keep the smaller. That top entry is already the smallest value under val, so one comparison is enough.
    6. Append currentMin to minStack. Both arrays now have the same length. Equal values are pushed too, so a minimum pushed twice is remembered twice.
    7. In pop, remove the last element of both arrays, so they stay the same size.
    8. In top, return the last element of stack.
    9. In getMin, return the last element of minStack.

    The solution in Swift

    class MinStack {
        var stack: [Int] = []
        var minStack: [Int] = []
    
        func push(_ val: Int) {
            stack.append(val)
            var currentMin = val
            if !minStack.isEmpty {
                currentMin = min(currentMin, minStack.last!)
            }
            minStack.append(currentMin)
        }
    
        func pop() {
            stack.removeLast()
            minStack.removeLast()
        }
    
        func top() -> Int {
            return stack.last!
        }
    
        func getMin() -> Int {
            return minStack.last!
        }
    }
    

    The Swift details worth a closer look:

    • last is an optional. array.last has type Int?, and it is nil for an empty array. The ! unwraps it and crashes the program if it is nil. Here that is safe, because the tests never call top or getMin on an empty stack, and push checks isEmpty before using minStack.last!.
    • removeLast() also needs a non-empty array. On an empty array it stops the program with a run-time error. Its result is the removed element, and pop ignores it. Swift accepts that because removeLast() is marked to allow an unused result.
    • The free function min. Inside the class, min(currentMin, minStack.last!) calls Swift's global min on two Int values. The class has no method of that name, so there is no clash.
    • A class, not a struct. The methods change stack and minStack. In a struct they would need to be marked mutating. A class is a reference type, so its methods can change its stored properties directly.
    • The type annotations. var stack: [Int] = [] says which kind of array the empty literal is. Without the annotation the compiler can't tell.

    Complexity

    Time: O(1) per operation. push does one comparison and two appends. pop removes two last elements. top and getMin read one last element. An append is amortized constant: now and then the array has to grow, but the cost averages out over many pushes.

    Space: O(n). For n values in the stack there are n entries in each array, so twice as many integers as a plain stack. That is the price of making getMin constant-time.

    Edge cases and common mistakes

    • Keeping one min variable. It works until the minimum is popped. Then the previous minimum is lost, and finding it again means scanning the stack.
    • Duplicate minimums. Push 5, 5, 7 and the arrays are [5, 5, 7] and [5, 5, 5]. After two pops, the stack still holds one 5, and getMin() gives 5. A version that pushes to the minimum stack only when a value is strictly smaller would lose the second 5 and answer wrongly here.
    • Forgetting to pop from both arrays. The two arrays must always have the same length. If pop only touches stack, getMin returns a minimum that is no longer in the stack.
    • Top and minimum are different things. After pushing 3, 1, 2, top() is 2 and getMin() is 1.
    • Extreme values. Values range over the whole 32-bit signed range, and Swift's Int is 64 bits, so -2147483648 and 2147483647 fit with room to spare. The code only compares values, and never adds them.

    Testing

    • Push -2, 0, -3, then getMin(), pop(), top(), getMin() gives -3 0 -2.
    • Push 5, 5, 7, then getMin(), pop(), pop(), getMin(), top() gives 5 5 5.
    • Push -2147483648 and 2147483647: getMin() is -2147483648 and top() is 2147483647.
    • Push 3, 1, 2: the arrays are [3, 1, 2] and [3, 1, 1].
    Keep going with Swift500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free