Start learning free

LESSON 398 OF 540 · STACK

Min Stack in TypeScript: O(1) Operations 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

    TYPESCRIPT · STACK

    Min Stack in TypeScript, explained

    Problem overview

    A stack gives values back in the opposite order they arrived. The class MinStack must behave like one, with two requirements: push(val) puts a number on top, pop() removes the top value, top() returns the top value without removing it, and getMin() returns the smallest value currently in the stack. Every operation has to run in constant time. pop, top and getMin are only called on a stack that isn't empty.

    Take the first example: push -2, 0 and -3, then call getMin(). The stack holds -2, 0 and -3, so the answer is -3. After pop() removes -3, top() returns 0 and getMin() returns -2. The output for the whole run is -3 0 -2.

    Approach: remember the minimum at every depth

    The obvious getMin scans the whole array, which costs O(n) per call. A single variable holding the smallest value seems to fix that, but it breaks on pop. After pushing -2, 0 and -3 the variable holds -3. Once -3 is popped, the variable should go back to -2, and nothing in the variable remembers that. With a single variable that keeps the smallest value pushed so far, getMin() still returns -3 after the pop.

    The fix rests on one observation: a value leaves the stack only after every value above it has left. So the values below it don't change while it is there, and the minimum of everything from the bottom up to that value can be computed once, when the value is pushed. Store it next to the value. A second array, minStack, does this: its entry at each depth is the smallest value at that depth and below. The top entry then describes the whole stack as it is now, and popping removes the top entry of both arrays together.

    Here is the state while the first example runs, with the top at the right:

    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]
    

    After the pop, the top of minStack is -2 again, with no searching.

    Step by step

    1. Declare stack as a number[] field, empty. Array push and pop both work at the end, so the end of the array is the top of the stack.
    2. Declare minStack the same way. It always has the same length as stack.
    3. In push, put val on this.stack first, as any stack would.
    4. Start currentMin at val. If the stack was empty, val is its only value and so its smallest. It is a let because the next step may lower it.
    5. Compare with the minimum below. If minStack isn't empty, its top entry is already the smallest of everything under val, 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. In pop, pop both arrays. Removing the top of minStack uncovers the minimum the stack had before that value arrived.
    8. In top, return the last element of stack, at index this.stack.length - 1. Reading it removes nothing.
    9. In getMin, return the last element of minStack.

    The solution in TypeScript

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

    A few TypeScript details:

    • Field initialisers. stack: number[] = []; declares the field and gives each new MinStack its own empty array, so two stacks never share their contents.
    • Types are only annotations. The : number and : void are erased when the code is compiled to JavaScript. They check the calls at compile time and do nothing at run time.
    • Empty arrays. pop() on an empty array returns undefined and reading stack[-1] also gives undefined, with no error. The constraints promise the stack isn't empty, so the lesson never relies on this.
    • Values near the limits. A number is a double, which holds every 32-bit integer exactly, so there is no overflow to worry about. Pushing 2147483647 and then -2147483648 gives -2147483648 from getMin().

    Complexity

    Time: O(1) per operation. Each method does a constant number of array reads, pushes and pops at the end of an array, plus one Math.min. The cost doesn't depend on how many values the stack holds.

    Space: O(n) for n values on the stack. Every value has one entry in each array, so the memory is twice that of a plain stack, which is still linear.

    Edge cases and common mistakes

    • Keeping one running minimum. It can't go back up after a pop, as the example shows. That is why the minimum is stored per depth.
    • Top and minimum differ. After pushing 3, 1 and 2, top() is 2 while getMin() is 1. Returning the same value from both is a mistake.
    • Duplicates. After pushing 5, 5, 7 and popping twice, the stack still holds a 5, so getMin() still returns 5. Because every push writes an entry into minStack, even when the new value equals the current minimum, each copy leaves with its own entry.
    • Forgetting one pop. If only stack is popped, the arrays get out of step and getMin describes values that are gone.
    • The first push. When minStack is empty, reading its last element gives undefined, and Math.min(val, undefined) is NaN. The length > 0 check avoids this.

    Testing

    • 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 TypeScript500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free