Largest Rectangle in Histogram Using Stack in DSA Python - 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 speed change when the histogram gets bigger?
Analyze the time complexity of the following code snippet.
def largest_rectangle_area(heights):
stack = []
max_area = 0
for i, h in enumerate(heights + [0]):
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()]
width = i if not stack else i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area
This code finds the largest rectangle area in a histogram by using a stack to track bars and calculate areas efficiently.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: A single loop goes through each bar once, and inside it, bars are popped from the stack when they are taller than the current bar.
- How many times: Each bar is pushed and popped at most once, so the total number of stack operations is proportional to the number of bars.
As the number of bars (n) increases, the algorithm processes each bar a limited number of times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 stack operations (push and pop combined) |
| 100 | About 200 stack operations |
| 1000 | About 2000 stack operations |
Pattern observation: The number of operations grows roughly in direct proportion to the number of bars.
Time Complexity: O(n)
This means the time to find the largest rectangle grows linearly with the number of bars in the histogram.
[X] Wrong: "Because there is a nested while loop inside the for 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 for every iteration of the outer loop, keeping the total operations linear.
Understanding this stack-based approach shows your ability to use data structures smartly to solve problems efficiently, a skill highly valued in coding interviews.
What if we removed the extra 0 added at the end of the heights list? How would the time complexity change?
Practice
Solution
Step 1: Understand the role of the stack
The stack is used to store indices of bars in increasing height order to efficiently find the largest rectangle.Step 2: Explain why increasing order is important
When a bar with smaller height appears, we pop from the stack to calculate areas with bars taller than the current one.Final Answer:
To keep track of bars' indices with increasing heights -> Option CQuick Check:
Stack stores indices in increasing height order [OK]
- Thinking stack stores heights instead of indices
- Assuming stack sorts bars by height
- Using stack to count bars instead of track indices
Solution
Step 1: Identify correct data structure for stack
In Python, a list is commonly used as a stack because it supports append() and pop() operations.Step 2: Check each option
stack = [] uses a list ([]), which is correct. stack = {} uses a dictionary ({}), not suitable. stack = None assigns None, not a stack. stack = () uses a tuple (()), which is immutable.Final Answer:
stack = [] -> Option DQuick Check:
List [] is correct stack initialization [OK]
- Using dictionary {} instead of list
- Using tuple () which is immutable
- Assigning None instead of a list
def largest_rectangle_area(heights):
stack = []
max_area = 0
for i, h in enumerate(heights + [0]):
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()]
width = i if not stack else i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area
print(largest_rectangle_area([2, 1, 5, 6, 2, 3]))Solution
Step 1: Trace the stack and area calculation
Iterate through heights with an extra 0 at the end to pop all bars. The largest rectangle area found is 10 (from bars with heights 5 and 6).Step 2: Confirm max area calculation
The max area is calculated as max(10, others), so final answer is 10.Final Answer:
10 -> Option AQuick Check:
Largest rectangle area = 10 [OK]
- Forgetting to add zero at end of heights
- Incorrect width calculation
- Not popping all bars from stack
def largest_rectangle_area(heights):
stack = []
max_area = 0
for i, h in enumerate(heights + [0]):
while stack and heights[stack[-1]] >= h:
height = heights[stack.pop()]
width = i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area
print(largest_rectangle_area([2, 1, 5, 6, 2, 3]))Solution
Step 1: Identify width calculation mistake
Width is calculated as i - stack[-1], but it should be i - stack[-1] - 1 to exclude the popped bar's index.Step 2: Confirm other parts
Adding zero at end is optional but common; comparison >= is acceptable; stack stores indices correctly.Final Answer:
Width calculation should be i - stack[-1] - 1 -> Option AQuick Check:
Width = i - stack[-1] - 1 [OK]
- Incorrect width calculation causing wrong area
- Forgetting to add zero at end (optional)
- Confusing stack contents (indices vs heights)
Solution
Step 1: Use stack to find largest rectangle
Traverse bars with an extra 0 at end. The largest rectangle is formed by heights 5, 4, 5 spanning width 3, area = 12, but a larger rectangle is formed by heights 6 spanning width 2, area = 12, and also by heights 6 spanning width 1, area = 6. However, the maximum area is 15 formed by heights 5, 4, 5, 1, 6 combined correctly.Step 2: Confirm no larger area exists
After careful calculation, the largest rectangle area is 15 (from bars at indices 2 to 4 with height 5).Final Answer:
15 -> Option BQuick Check:
Max area = 15 from bars 5,4,5 [OK]
- Ignoring width when calculating area
- Not adding zero height at end to pop all bars
- Miscomputing width boundaries
Ready to prove it?
Pass the test and this pattern moves unknown
