Start learning free

LESSON 394 OF 536 · STACK

Min Stack in JavaScript: O(1) getMin with Two Arrays

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

    JAVASCRIPT · STACK

    Min Stack in JavaScript, explained

    Problem overview

    A stack gives values back in reverse order: the last value pushed is the first popped. In JavaScript an array already behaves like one, because push and pop both work at its end. The lesson asks for a class, MinStack, with four methods: push(val), pop(), top() and getMin(). The last one must return the smallest value currently in the stack, and every method has to run in constant time.

    Take the first example: push -2, push 0, push -3. The stack holds -2, 0 and -3, with -3 on top, so getMin() returns -3. After pop() the stack holds -2 and 0. Now top() returns 0 and getMin() returns -2: the minimum that was there before -3 arrived has come back.

    The constraints say pop, top and getMin are only called on a non-empty stack, so the code needs no empty-stack checks.

    Approach: remember the minimum at every depth

    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 have to be recomputed from what remains.

    The fix comes from one observation. A value leaves the stack only after every value above it has left, so the values below it never change while it is in the stack. The minimum of "this value and everything below it" can therefore be computed once, when the value is pushed, and stored next to it.

    So the class keeps two arrays of the same length. stack holds the values. minStack holds, at each depth, the smallest value from the bottom up to that depth. The last entry of minStack always describes the whole stack as it is now, so getMin only reads it. Popping removes the last entry of both arrays, which uncovers the previous minimum automatically.

    Here are the two arrays during the first example:

    push -2: stack [-2]          minStack [-2]
    push 0:  stack [-2,0]        minStack [-2,-2]
    push -3: stack [-2,0,-3]     minStack [-2,-2,-3]
    pop:     stack [-2,0]        minStack [-2,-2]
    

    Pushing 0 repeats the minimum -2 in minStack, because -2 is still the smallest value at that depth. After the pop, the last entry of minStack is -2 again, which is what getMin() returns.

    Step by step

    1. Create this.stack in the constructor as an empty array. The end of the array is the top of the stack.
    2. Create this.minStack as another empty array. Its entry at each depth is the smallest value of that depth and all the depths below it.
    3. In push, push val onto this.stack first, as on any stack.
    4. Start a candidate minimum, currentMin, equal to val. It is declared with let because the next step may lower it. If the stack was empty, val is the only value, so it is the minimum.
    5. Compare with the old minimum. If minStack is not empty, its last entry is already the smallest of everything below val, so one Math.min against that entry is enough.
    6. Push currentMin onto minStack. Both arrays now have the same length. Equal values are pushed too, so a minimum that was pushed twice is remembered twice.
    7. In pop, pop from both arrays, one line each. They must stay the same size.
    8. In top, return the last element of this.stack, at index length - 1. Reading it removes nothing.
    9. In getMin, return the last element of this.minStack.

    The solution in JavaScript

    class MinStack {
        constructor() {
            this.stack = [];
            this.minStack = [];
        }
    
        push(val) {
            this.stack.push(val);
            let currentMin = val;
            if (this.minStack.length > 0) {
                currentMin = Math.min(currentMin, this.minStack[this.minStack.length - 1]);
            }
            this.minStack.push(currentMin);
        }
    
        pop() {
            this.stack.pop();
            this.minStack.pop();
        }
    
        top() {
            return this.stack[this.stack.length - 1];
        }
    
        getMin() {
            return this.minStack[this.minStack.length - 1];
        }
    }
    

    A few JavaScript details are worth knowing:

    • State lives on this. Each new MinStack() runs the constructor, so every instance gets its own two arrays. Without this., a name like stack would be a plain local variable that disappears when the constructor ends.
    • Array push and pop are amortized constant time. Both work at the end of the array. Using shift or unshift at the front would be a trap, because they move every element.
    • Math.min takes numbers. Values from -2147483648 to 2147483647 are far inside the range where JavaScript numbers are exact integers, so no overflow can happen here.
    • Methods that return nothing. push and pop have no return, so a call evaluates to undefined. Only top and getMin produce values.
    • The last element. arr[arr.length - 1] reads it. A negative index like arr[-1] does not count from the end in JavaScript: it looks for a property named "-1" and gives undefined. (arr.at(-1) does count from the end.)

    Complexity

    Time: O(1) per operation. Each method does a fixed number of array reads, writes, one comparison, and at most one push or pop on each array.

    Space: O(n). For n values on the stack, there are n entries in stack and n in minStack, so twice as many numbers as a plain stack, still linear.

    Edge cases and common mistakes

    • Duplicates of the minimum. Pushing 5, 5, 7 gives minStack [5, 5, 5]. After two pops the stack holds one 5 and getMin() still returns 5. A version that stores a new minimum only when it is strictly smaller would lose the second 5 and answer wrongly here.
    • A single value. After push(3), both top() and getMin() return 3. The if in push is skipped, and currentMin stays equal to val.
    • Top and minimum differ. After pushing 3, 1, 2, top() returns 2 and getMin() returns 1. After two pops, only 3 is left and it is the minimum again.
    • Popping only one array. The two arrays would drift out of step, and getMin would describe the wrong depth.
    • Using a single minimum variable. It breaks as soon as the minimum is popped, because nothing remembers the previous one.
    • Scanning in getMin. Using Math.min(...this.stack) is correct but costs O(n) per call. With up to 200,000 calls, that is too slow, and a very long stack can also exceed the limit on how many arguments a spread call may pass.

    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 3, then top() and getMin(), push 1 and 2, then top(), getMin(), pop(), pop(), getMin() gives 3 3 2 1 3.
    • Push -2147483648 then 2147483647: getMin() gives -2147483648 and top() gives 2147483647.
    Keep going with JavaScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free