Start learning free

LESSON 398 OF 540 · STACK

Min Stack in Scala: O(1) getMin with Two Stacks

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

    SCALA · STACK

    Min Stack in Scala, explained

    Problem overview

    The class MinStack behaves like an ordinary stack: push puts an integer on top, pop removes the top value, and top reads it without removing it. It has one extra operation, getMin, which returns the smallest value currently in the stack. Every operation must run in constant time, and pop, top and getMin are only called on a non-empty stack.

    Take the first example: push -2, push 0, push -3. The stack holds -2, 0 and -3, with -3 on top, so getMin() gives -3. After pop() the stack holds -2 and 0, so top() gives 0 and getMin() gives -2. The output is -3 0 -2. The third example shows that the top and the minimum are different questions: after pushing 3, 1 and 2, the top is 2 and the minimum is 1.

    Approach: remember the minimum under every value

    The obvious getMin scans the whole stack, which costs O(n) per call. Keeping a single variable with the smallest value fails as soon as that value is popped: the variable would need the second smallest, and finding it means scanning again.

    The fix is to record the answer while the information is available. A value leaves the stack only after every value above it has left, so the values below it cannot change while it is in the stack. The smallest value from the bottom up to a given position can therefore be computed once, when that position is pushed, and never changes.

    So the class keeps two stacks of equal height. The first holds the values. The second holds, at each depth, the smallest value from the bottom up to that depth. The top of the second stack always describes the whole stack as it is now. Popping removes the top of both, and the minimum from before that value arrived is uncovered automatically.

    Pushing -2, 0 and -3 builds these two stacks, bottom first:

    push(-2): stack=List(-2) minStack=List(-2)
    push(0): stack=List(-2, 0) minStack=List(-2, -2)
    push(-3): stack=List(-2, 0, -3) minStack=List(-2, -2, -3)
    

    After the pop, the top of minStack is -2 again, which is exactly what the example expects.

    Step by step

    1. Declare the value stack as a val holding a scala.collection.mutable.Stack[Int]. The val fixes which stack the name refers to, while the stack itself stays mutable.
    2. Declare the second stack the same way, as minStack. It will always have as many entries as stack.
    3. Start push. The parameter is called value because val is a Scala keyword. First put the value on stack.
    4. Start the candidate minimum in a var called currentMin, set to value. If the stack was empty, that value is the only one and so the smallest.
    5. Compare with what is below. If minStack is not empty, its top is already the smallest of everything underneath, so one math.min against it is enough.
    6. Push currentMin onto minStack. Equal values are pushed too, so a minimum that was pushed twice is remembered twice.
    7. Write pop. Pop both stacks, one line each, so they stay the same height.
    8. Write top. It returns the top of stack without removing it.
    9. Write getMin. It returns the top of minStack.

    The solution in Scala

    class MinStack {
        val stack = scala.collection.mutable.Stack[Int]()
        val minStack = scala.collection.mutable.Stack[Int]()
        def push(value: Int): Unit = {
            stack.push(value)
            var currentMin = value
            if (minStack.nonEmpty) {
                currentMin = math.min(currentMin, minStack.top)
            }
            minStack.push(currentMin)
        }
        def pop(): Unit = {
            stack.pop()
            minStack.pop()
        }
        def top(): Int = {
            return stack.top
        }
        def getMin(): Int = {
            return minStack.top
        }
    }
    

    The Scala details worth a closer look:

    • mutable.Stack. Scala's default collections are immutable, so the code names the mutable one in full. push and pop() work at the top, and top reads the top without removing it. Printing a stack lists the top first: after pushing 1, 2 and 3, println(stack) shows Stack(3, 2, 1).
    • Constructing without new. Stack[Int]() calls the companion object's apply, which builds an empty stack of integers.
    • return is optional. The last expression of a method is its value, so stack.top alone would do in top and getMin. The lesson's code spells return out, which is also correct.
    • Unit. push and pop return Unit, Scala's "nothing useful" result. The stack's own pop() returns the removed value, but the lesson's pop method does not pass it on.
    • Int covers the range. The values go up to 2^31 - 1, the largest Int, and down to -2^31. math.min only compares, so nothing overflows: pushing Int.MaxValue and then Int.MinValue gives a getMin of -2147483648 and a top of -2147483648, and after one pop getMin is 2147483647.
    • Empty stacks throw. Calling top on an empty mutable.Stack raises java.util.NoSuchElementException: head of empty Stack. The lesson promises never to do that, so no check is needed.

    Complexity

    Time: O(1) per operation. push does two stack pushes and one comparison, pop two pops, and top and getMin one read each. None of them looks at more than the top of a stack.

    Space: O(n). For n values in the stack, the second stack holds n more integers. That is twice the memory of a plain stack, which is the price for constant-time minimums.

    Edge cases and common mistakes

    • Duplicate minimums. Pushing 5, 5 and 7 and then popping twice leaves a 5, so the minimum is still 5. Because minStack gets an entry for every push, each copy of the 5 has its own record. A design that stored the minimum only when it strictly decreases would lose the second 5 and answer wrongly here.
    • Top and minimum are different. After pushing 3, 1, 2, the top is 2 and the minimum is 1. Returning one for the other fails the third example.
    • Forgetting to pop minStack. The stacks fall out of step, and getMin reports a minimum that has already left.
    • A single variable for the minimum. It works until the minimum is popped, and then has nothing to fall back on.
    • A single element. With one value pushed, top and getMin both return it. The nonEmpty check is what keeps push from reading an empty minStack.

    Testing

    Using one new MinStack for each line, the outputs of top and getMin in order are:

    • push(-2), push(0), push(-3), getMin(), pop(), top(), getMin() gives -3 0 -2.
    • push(5), push(5), push(7), getMin(), pop(), pop(), getMin(), top() gives 5 5 5.
    • push(3), top(), getMin(), push(1), push(2), top(), getMin(), pop(), pop(), getMin() gives 3 3 2 1 3.
    Keep going with Scala500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free