Largest Rectangle in Histogram Using Stack in DSA Go - Time & Space Complexity
We want to understand how the time needed to find the largest rectangle in a histogram changes as the number of bars grows.
How does the algorithm's work increase when the histogram gets bigger?
Analyze the time complexity of the following code snippet.
func largestRectangleArea(heights []int) int {
stack := []int{}
maxArea := 0
for i := 0; i <= len(heights); i++ {
var h int
if i == len(heights) {
h = 0
} else {
h = heights[i]
}
for len(stack) > 0 && h < heights[stack[len(stack)-1]] {
height := heights[stack[len(stack)-1]]
stack = stack[:len(stack)-1]
var width int
if len(stack) == 0 {
width = i
} else {
width = i - stack[len(stack)-1] - 1
}
area := height * width
if area > maxArea {
maxArea = area
}
}
stack = append(stack, i)
}
return maxArea
}
This code finds the largest rectangle area in a histogram by using a stack to keep track of bars.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: A single loop over all bars with inner while-loop popping from the stack.
- How many times: Each bar is pushed and popped at most once, so total operations scale with number of bars.
As the number of bars increases, the algorithm processes each bar a limited number of times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 (push and pop each bar once) |
| 100 | About 200 |
| 1000 | About 2000 |
Pattern observation: The operations grow roughly in a straight line with input size.
Time Complexity: O(n)
This means the time to find the largest rectangle grows directly in proportion to the number of bars.
[X] Wrong: "Because there is a nested loop, the time complexity must be O(n²)."
[OK] Correct: The inner loop does not run fully for each outer loop step; each bar is pushed and popped only once, so total work is linear.
Understanding this algorithm shows you can use stacks smartly to solve problems efficiently, a skill valued in many coding challenges.
"What if we used a simple nested loop to check all rectangles instead of a stack? How would the time complexity change?"
Practice
Solution
Step 1: Understand stack usage
The stack stores indices of bars in increasing height order to find boundaries efficiently.Step 2: Purpose in algorithm
This helps quickly calculate the largest rectangle when a smaller height bar is found.Final Answer:
To keep track of bars' indices with increasing heights -> Option CQuick Check:
Stack tracks increasing bars = B [OK]
- Thinking stack sorts bars by height
- Using stack to store areas instead of indices
- Ignoring the order of bars in stack
Solution
Step 1: Identify stack type
The stack holds indices, which are integers, so it should be a slice of int.Step 2: Check syntax correctness
"var stack []int" declares an empty slice of int, suitable for stack operations.Final Answer:
var stack []int -> Option AQuick Check:
Stack as []int slice = C [OK]
- Using map instead of slice for stack
- Declaring stack as int instead of slice
- Using string slice for integer indices
func largestRectangleArea(heights []int) int {
stack := []int{}
maxArea := 0
for i := 0; i <= len(heights); i++ {
var h int
if i == len(heights) {
h = 0
} else {
h = heights[i]
}
for len(stack) > 0 && h < heights[stack[len(stack)-1]] {
height := heights[stack[len(stack)-1]]
stack = stack[:len(stack)-1]
var width int
if len(stack) == 0 {
width = i
} else {
width = i - stack[len(stack)-1] - 1
}
area := height * width
if area > maxArea {
maxArea = area
}
}
stack = append(stack, i)
}
return maxArea
}
fmt.Println(largestRectangleArea([]int{2, 1, 5, 6, 2, 3}))Solution
Step 1: Trace stack and area calculation
Process bars: max area found is 10 (from bars 5 and 6 with width 2).Step 2: Confirm max area
No larger rectangle found; 10 is the largest rectangle area.Final Answer:
10 -> Option DQuick Check:
Largest rectangle area = 10 [OK]
- Forgetting to add a zero height bar at end
- Incorrect width calculation after popping stack
- Not updating maxArea correctly
for i := 0; i < len(heights); i++ {
for len(stack) > 0 && heights[i] < heights[stack[len(stack)-1]] {
height := heights[stack[len(stack)-1]]
stack = stack[:len(stack)-1]
width := i
if len(stack) > 0 {
width = i - stack[len(stack)-1] - 1
}
area := height * width
if area > maxArea {
maxArea = area
}
}
stack = append(stack, i)
}
return maxAreaSolution
Step 1: Check loop boundary
The loop misses processing the last bars fully without a sentinel zero height at the end.Step 2: Fix loop to include sentinel
Loop should run until i <= len(heights) and treat i == len(heights) as height 0 to pop all remaining bars.Final Answer:
Loop should run until i <= len(heights) with a sentinel zero height -> Option AQuick Check:
Loop boundary with sentinel zero fixes bug [OK]
- Not adding sentinel zero height
- Incorrect width calculation when stack empty
- Resetting maxArea inside loop incorrectly
Solution
Step 1: Analyze bars and stack operations
Largest rectangle is formed by bars with min height 5 spanning width 3 (indices 2 to 4: heights 5,4,5), area = 5*3 = 15.Step 2: Confirm no larger area
Other rectangles like 4*3=12 or 2*5=10 are smaller; 15 is maximum.Final Answer:
15 -> Option BQuick Check:
Max rectangle area = 15 [OK]
- Calculating width incorrectly
- Missing wider rectangle spanning multiple bars
- Not popping all bars at the end
Ready to prove it?
Pass the test and this pattern moves unknown
