Given heights, the heights of adjacent bars each of width 1 in a histogram, return the area of the largest rectangle that fits entirely inside the histogram.
Input: heights = [2,1,5,6,2,3] Output: 10 The rectangle over the bars of height 5 and 6 has width 2 and area 10.
Input: heights = [2,4] Output: 4 The bar of height 4 alone (area 4) beats both bars at height 2 (area 4 as well).
- 1 <= heights.length <= 10^5 - 0 <= heights[i] <= 10^4
Each bar can be the height of a rectangle that extends left and right as far as no shorter bar blocks it. So the widest rectangle of height heights[i] reaches from just after the previous smaller bar to just before the next smaller bar. A single monotonic increasing stack finds those boundaries and the answer in O(n).
“Must the rectangle be axis-aligned and contiguous?”
Yes — it spans a contiguous range of bars and its height is the shortest bar in that range.
“Can bars have height 0?”
Yes; such a bar contributes a zero-height (zero-area) rectangle.
For each bar, the tallest rectangle using it as the height extends until a shorter bar on either side.
I keep an increasing stack of indices; when a shorter bar arrives, I pop and compute that bar's rectangle.
The width comes from the current index and the new stack top — the previous smaller bar.
Worked example — heights = [2, 1, 5, 6, 2, 3]
the tallest rectangle is heights 5 and 6 over width 2 -> area 10 answer: 10
Bar i's widest rectangle at height heights[i] runs from just after the previous strictly-smaller bar to just before the next strictly-smaller bar.
The incoming shorter bar is the next-smaller boundary for everything taller on the stack, so pop and measure those rectangles then.
After popping, the new top is the popped bar's previous-smaller boundary, so width = i - newTop - 1 (or i if the stack is empty).
| Expand each bar | Monotonic stack | |
|---|---|---|
| Idea | For each bar, extend left/right while >= its height | Increasing index stack; measure on a shorter bar |
| Time | O(n^2) | O(n) |
| Space | O(1) | O(n) |
Full code is in the Approaches selector below.
Key takeaway
Each bar's widest rectangle runs between its previous- and next-smaller bars. An increasing index stack finds those on the fly: pop when a shorter bar arrives, take height x (i - newTop - 1), and use a trailing 0 to flush. O(n) time.
stack = []; best = 0
for i in 0 .. n (heights[n] = 0 sentinel):
while stack and heights[stack.top] > heights[i]:
h = heights[stack.pop]
w = stack ? i - stack.top - 1 : i
best = max(best, h * w)
stack.push(i)
return best