Min Stack Design in DSA Typescript - Time & Space Complexity
We want to understand how fast the Min Stack operations run as the number of elements grows.
How does the time to push, pop, or get the minimum change when the stack gets bigger?
Analyze the time complexity of the following Min Stack implementation.
class MinStack {
private stack: number[] = [];
private minStack: number[] = [];
push(x: number): void {
this.stack.push(x);
if (!this.minStack.length || x <= this.minStack[this.minStack.length - 1]) {
this.minStack.push(x);
}
}
pop(): void {
const val = this.stack.pop();
if (val !== undefined && val === this.minStack[this.minStack.length - 1]) {
this.minStack.pop();
}
}
top(): number | undefined {
return this.stack[this.stack.length - 1];
}
getMin(): number | undefined {
return this.minStack[this.minStack.length - 1];
}
}
This code keeps track of the minimum value at each step using a second stack.
Look at the main repeated actions when using the stack.
- Primary operation: push and pop on two stacks (main and min stack)
- How many times: once per element added or removed
Each push or pop does a fixed number of steps regardless of stack size.
| Input Size (n) | Approx. Operations per push/pop |
|---|---|
| 10 | 2 to 3 operations |
| 100 | 2 to 3 operations |
| 1000 | 2 to 3 operations |
Pattern observation: The number of steps per operation stays about the same no matter how big the stack is.
Time Complexity: O(1)
This means each push, pop, top, or getMin operation takes the same small amount of time no matter how many elements are in the stack.
[X] Wrong: "Getting the minimum value takes longer as the stack grows because we have to check all elements."
[OK] Correct: The min stack keeps track of minimums at each step, so getMin just looks at the top of minStack, which is always fast.
Understanding how to keep track of extra information efficiently is a key skill. This design shows how to keep minimums handy without slowing down the main operations.
What if we stored minimum values only when they change instead of every time? How would that affect the time complexity?
Practice
Min Stack data structure?Solution
Step 1: Understand the Min Stack functionality
A Min Stack is designed to keep track of the minimum element efficiently while supporting normal stack operations.Step 2: Identify the key feature
The key feature is retrieving the minimum element in constant time, which normal stacks cannot do efficiently.Final Answer:
To retrieve the minimum element in constant time -> Option AQuick Check:
Min Stack = constant time minimum retrieval [OK]
- Thinking it sorts elements automatically
- Confusing with normal stack behavior
- Assuming it removes duplicates
x onto a Min Stack class?Solution
Step 1: Understand push method requirements
The push method should accept a number and return nothing (void) because it adds an element to the stack.Step 2: Match method signature
push(x: number): void matches: it takes a number and returns void, which is correct for push operation.Final Answer:
push(x: number): void -> Option CQuick Check:
Push method signature = push(x: number): void [OK]
- Using wrong parameter type like string
- Returning number instead of void
- Missing parameter in method
const minStack = new MinStack(); minStack.push(3); minStack.push(5); minStack.push(2); minStack.push(1); minStack.pop(); const min = minStack.getMin(); console.log(min);What will be printed?
Solution
Step 1: Trace push and pop operations
Push 3, 5, 2, 1 onto stack. Stack is [3,5,2,1]. Then pop removes 1. Stack is now [3,5,2].Step 2: Find minimum after pop
Minimum among [3,5,2] is 2, so getMin() returns 2.Final Answer:
2 -> Option AQuick Check:
Min after pop = 2 [OK]
- Forgetting pop removes last pushed element
- Assuming min stays 1 after pop
- Confusing stack order
class MinStack {
stack: number[] = [];
minStack: number[] = [];
push(x: number): void {
this.stack.push(x);
if (this.minStack.length === 0 || x <= this.minStack[this.minStack.length - 1]) {
this.minStack.push(x);
}
}
pop(): void {
const val = this.stack.pop();
if (val === this.minStack[this.minStack.length - 1]) {
this.minStack.pop();
}
}
getMin(): number {
return this.minStack[this.minStack.length - 1];
}
}Solution
Step 1: Analyze pop() method
pop() calls this.stack.pop() which can return undefined if stack is empty, then compares with minStack top without check.Step 2: Identify missing empty check
There is no check if stack is empty before pop, so val can be undefined causing incorrect comparison or error.Final Answer:
pop() does not handle empty stack case -> Option BQuick Check:
pop() missing empty check = bug [OK]
- Ignoring empty stack before pop
- Assuming push always pushes to minStack
- Misreading getMin index
push, pop, top, and getMin all in constant time. Which approach below correctly achieves this?Solution
Step 1: Understand time complexity requirements
All operations must be constant time, so scanning or traversing is not allowed.Step 2: Identify data structure approach
Using two stacks, one for elements and one for tracking minimums, allows constant time push, pop, and getMin.Final Answer:
Use two stacks: one for all elements, one for current mins -> Option DQuick Check:
Two stacks = constant time min tracking [OK]
- Using scanning which is not constant time
- Using queue which is not stack
- Using linked list traversal for min
Ready to prove it?
Pass the test and this pattern moves unknown
