Find the largest rectangle that fits under histogram bars.
Start with the concrete trace below. It shows the state the algorithm must carry as it runs.
Problem trace
Largest Rectangle in Histogram: Keep increasing (start,height) candidates; the first shorter bar closes each taller candidate at its exact maximal width.
Input and goalFind the largest rectangle that fits under histogram bars.
Initialize the increasing stackStart with an empty stack, best = 0, and scan the real histogram before an appended zero sentinel.
scan2011526324350 sentinel6
unresolved stackemptytop →
currentindex 0, height 2best0
Push height 2Nothing taller is waiting, so push (start 0, height 2).
scan2011526324350 sentinel6
unresolved stack0: height 2top →
actionpush (0,2)best0
Height 1 closes height 2At index 1, 2 > 1, so pop (0,2): area = 2 * (1 - 0) = 2. Carry start = 0 for the shorter bar.
Push the sentinelAfter the pop loop, the implementation pushes (0,0). It contributes no area but leaves the loop transition fully represented.
201152632435scan0 sentinel6
unresolved stack0: height 0top →
actionpush (0,0)best10
Return the largest closed areaEvery bar has now met its first shorter right boundary. The largest recorded rectangle has height 5, width 2, and area 10.
Use it when each element needs the widest contiguous span for which it remains the limiting minimum, and the first smaller boundary finalizes that span.
Keep true
Stack heights are nondecreasing; each pair (start,height) can extend from start through index-1, and every popped height receives index as its first smaller right boundary while propagating its start to the incoming shorter bar.
Reuse it
When the first violating element determines an unresolved candidate’s right boundary, keep candidates monotone and finalize them in reverse order; propagate the oldest valid start across every pop.
Read it this way: Start with an empty stack, best = 0, and scan the real histogram before an appended zero sentinel. Step through the frames to watch the state change. The last frame shows the answer or the stopping condition.
Pattern: Increasing stack.
Simple idea: Save increasing heights and where each height began. A shorter bar ends the
range of every taller saved bar. Calculate those areas when they end.
def largest_rectangle_area(heights: list[int]) -> int: stack: list[tuple[int, int]] = [] best = 0 for index, height in enumerate(heights + [0]): start = index while stack and stack[-1][1] > height: start, old_height = stack.pop() best = max(best, old_height * (index - start)) stack.append((start, height)) return best