Largest Rectangle in Histogram Using Stack in DSA Rust - Time & Space Complexity
We want to know how the time needed to find the largest rectangle in a histogram changes as the histogram grows.
How does the algorithm's work increase when we add more bars?
Analyze the time complexity of the following code snippet.
fn largest_rectangle_area(heights: Vec<i32>) -> i32 {
let mut stack = Vec::new();
let mut max_area = 0;
let mut i = 0;
while i <= heights.len() {
let h = if i == heights.len() { 0 } else { heights[i] };
while !stack.is_empty() && h < heights[*stack.last().unwrap()] {
let height = heights[stack.pop().unwrap()];
let width = if stack.is_empty() { i as i32 } else { (i - stack.last().unwrap() - 1) as i32 };
max_area = max_area.max(height * width);
}
stack.push(i);
i += 1;
}
max_area
}
This code finds the largest rectangle area in a histogram by using a stack to track bars.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop runs through each bar once, and the inner while loop pops bars from the stack.
- How many times: Each bar is pushed and popped at most once, so total operations grow roughly with the number of bars.
As the number of bars increases, the total steps increase roughly in a straight line.
| 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 linearly with input size.
Time Complexity: O(n)
This means the time to find the largest rectangle grows in direct proportion to the number of bars.
[X] Wrong: "Because there is a nested while loop, the time complexity must be O(n²)."
[OK] Correct: Each bar is pushed and popped only once, so the inner loop does not run n times for each outer loop iteration.
Understanding this stack-based approach shows you how to use data structures smartly to solve problems efficiently, a skill valued in many coding challenges.
"What if we used a simple nested loop checking all rectangles instead of a stack? How would the time complexity change?"
Practice
Solution
Step 1: Understand stack usage in the problem
The stack stores indices of bars in increasing height order to find boundaries for rectangles.Step 2: Explain why this helps
This allows quick calculation of largest rectangle areas when a smaller height bar is found.Final Answer:
To keep track of bars' indices with increasing heights -> Option DQuick Check:
Stack tracks indices with increasing heights = C [OK]
- Thinking stack sorts bars by height
- Using stack to store areas directly
- Ignoring the order of bars in stack
Solution
Step 1: Identify correct stack type for indices
Indices are usize type, so stack must be Vec<usize>.Step 2: Check mutability and initialization
Stack must be mutable and initialized empty with Vec::new().Final Answer:
let mut stack: Vec<usize> = Vec::new(); -> Option AQuick Check:
Mutable Vec of usize initialized empty = B [OK]
- Using wrong type like i32 for indices
- Not making stack mutable
- Using Vec::with_capacity(0) without mut
fn largest_rectangle_area(heights: Vec<i32>) -> i32 {
let mut stack: Vec<usize> = Vec::new();
let mut max_area = 0;
let mut i = 0;
while i <= heights.len() {
let h = if i == heights.len() { 0 } else { heights[i] };
if stack.is_empty() || h >= heights[*stack.last().unwrap()] {
stack.push(i);
i += 1;
} else {
let top = stack.pop().unwrap();
let width = if stack.is_empty() { i } else { i - stack.last().unwrap() - 1 };
max_area = max_area.max(heights[top] * width as i32);
}
}
max_area
}
fn main() {
println!("{}", largest_rectangle_area(vec![2, 1, 5, 6, 2, 3]));
}Solution
Step 1: Trace stack and area calculation
For heights [2,1,5,6,2,3], largest rectangle area is 10 (from bars 5 and 6 with width 2).Step 2: Confirm max_area value
Code correctly calculates max_area as 10 after processing all bars.Final Answer:
10 -> Option BQuick Check:
Largest rectangle area = 10 [OK]
- Confusing max area with sum of heights
- Miscomputing width when popping stack
- Forgetting to add sentinel zero height
fn largest_rectangle_area(heights: Vec<i32>) -> i32 {
let mut stack: Vec<usize> = Vec::new();
let mut max_area = 0;
let mut i = 0;
while i < heights.len() {
if stack.is_empty() || heights[i] >= heights[*stack.last().unwrap()] {
stack.push(i);
i += 1;
} else {
let top = stack.pop().unwrap();
let width = if stack.is_empty() { i } else { i - stack.last().unwrap() - 1 };
max_area = max_area.max(heights[top] * width as i32);
}
}
max_area
}Solution
Step 1: Identify width calculation error
Width should be i - stack.last().unwrap() - 1 to exclude the popped bar's index.Step 2: Explain fix impact
Without subtracting 1, width is overcounted, causing wrong area calculation.Final Answer:
Subtract 1 from width calculation: i - stack.last().unwrap() - 1 -> Option AQuick Check:
Correct width calculation subtracts 1 [OK]
- Missing -1 in width calculation
- Not handling sentinel zero height
- Incorrect while loop condition
Solution
Step 1: Use stack to track indices with increasing heights
Push indices while heights increase; pop when current height is less to calculate areas.Step 2: Calculate largest rectangle area
Largest rectangle area is 12 (from bars at indices 2,3,4 with min height 4 and width 3).Final Answer:
12 -> Option CQuick Check:
Stack helps find max area 12 [OK]
- Calculating area without considering width correctly
- Ignoring bars after popping stack
- Not using sentinel zero height to flush stack
Ready to prove it?
Pass the test and this pattern moves unknown
