Min Stack Design in DSA Go - Time & Space Complexity
We want to understand how fast the Min Stack operations run as the stack grows.
How does the time to push, pop, or get the minimum change with more elements?
Analyze the time complexity of the following Min Stack implementation.
type MinStack struct {
stack []int
minStack []int
}
func (ms *MinStack) Push(val int) {
ms.stack = append(ms.stack, val)
if len(ms.minStack) == 0 || val <= ms.minStack[len(ms.minStack)-1] {
ms.minStack = append(ms.minStack, val)
}
}
func (ms *MinStack) Pop() {
if len(ms.stack) == 0 {
return
}
top := ms.stack[len(ms.stack)-1]
ms.stack = ms.stack[:len(ms.stack)-1]
if top == ms.minStack[len(ms.minStack)-1] {
ms.minStack = ms.minStack[:len(ms.minStack)-1]
}
}
func (ms *MinStack) GetMin() int {
if len(ms.minStack) == 0 {
return -1 // or error
}
return ms.minStack[len(ms.minStack)-1]
}
This code keeps two stacks: one for all values and one for current minimums.
Look at the main operations and how often they repeat.
- Primary operation: Push and Pop each add or remove one element from stacks.
- How many times: Each operation works on the top element only, no loops inside.
Each push or pop does a fixed number of steps regardless of stack size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 1 step per push/pop |
| 100 | About 1 step per push/pop |
| 1000 | About 1 step per push/pop |
Pattern observation: The time per operation stays the same no matter how big the stack is.
Time Complexity: O(1)
This means each push, pop, or get minimum takes the same short time no matter how many items are in the stack.
[X] Wrong: "Finding the minimum must check all elements, so it takes longer as stack grows."
[OK] Correct: The min stack keeps track of minimums as we go, so getting the minimum is always quick without scanning all elements.
Understanding how to keep track of extra information efficiently is a great skill. It shows you can design smart data structures that keep operations fast.
"What if we stored minimums only when they change, instead of every push? How would that affect time complexity?"
Practice
Min Stack data structure?Solution
Step 1: Understand Min Stack functionality
A Min Stack is designed to keep track of the minimum element efficiently.Step 2: Identify the key feature
It allows retrieving the minimum element in constant time, unlike a normal stack.Final Answer:
To retrieve the minimum element in constant time -> Option AQuick Check:
Min Stack = Fast minimum retrieval [OK]
- Thinking it sorts elements
- Confusing with queue behavior
- Assuming it reverses elements
Solution
Step 1: Check struct fields for Min Stack
Min Stack needs slices to store all values and track minimums at each push.Step 2: Validate field types
Bothvaluesandminsshould be slices of int for stack behavior.Final Answer:
type MinStack struct { values []int; mins []int } -> Option CQuick Check:
Two int slices = correct Min Stack struct [OK]
- Using map instead of slice for values
- Using int instead of slice for mins
- Mixing types between fields
ms := MinStack{}
ms.Push(3)
ms.Push(5)
ms.Push(2)
ms.Push(1)
ms.Pop()
fmt.Println(ms.GetMin())Solution
Step 1: Trace Push operations and minimums
Push 3 (min=3), Push 5 (min=3), Push 2 (min=2), Push 1 (min=1).Step 2: Pop removes last pushed (1), so min updates to previous min (2)
After pop, minimum is 2.Final Answer:
2 -> Option AQuick Check:
Pop last min 1, new min = 2 [OK]
- Assuming min stays 1 after pop
- Confusing push order
- Not updating min stack on pop
Pop method:
func (ms *MinStack) Pop() {
ms.values = ms.values[:len(ms.values)-1]
ms.mins = ms.mins[:len(ms.mins)]
}Solution
Step 1: Analyze slice operations in Pop
Values slice is shortened by one element correctly.Step 2: Check mins slice operation
Mins slice is sliced to full length, so it is not shortened, causing mismatch.Final Answer:
The mins slice is not shortened on pop -> Option DQuick Check:
mins slice must shrink with values [OK]
- Forgetting to shrink mins slice
- Assuming Pop returns value
- Clearing slices fully on pop
Push, Pop, Top, and GetMin all in O(1) time. Which approach below correctly handles the minimum tracking when pushing a new element x?Solution
Step 1: Understand min tracking logic
Each push must record the minimum so far to allow O(1) GetMin.Step 2: Compare new element with current min
Push the smaller of x and current min onto mins slice to keep track.Final Answer:
Push x to values; push min(x, current min) to mins -> Option BQuick Check:
Always push min(x, current min) to mins [OK]
- Pushing x only if smaller causes missing mins
- Not updating mins on every push
- Ignoring mins slice updates
Ready to prove it?
Pass the test and this pattern moves unknown
