2Brute2Best
🔥0

Largest Rectangle in Histogram

Hard·LeetCode #84Basic Anchor
LeetCode
30-Second Mental ModelMonotonic Stack (contribution / boundaries)
Complexity:O(N²)→O(N)
Key Insight

Keep a stack of indices with increasing heights. When bar i is shorter than (or equal to) the top, the top's rectangle can't go past i, so i is its right wall. Pop it. The bar now under it is shorter than it (the stack is increasing), so that is its left wall. Area = height × (i − left − 1). A fake bar of height 0 at i = n pops whatever is left.

Core Validity Invariant

The heights on the stack always increase from bottom to top. So the bar just under any bar is the nearest bar to its left that is shorter: its left wall.

01

Problem

You are given an array heights that describes a bar chart. Bar i has width 1 and height heights[i], and the bars stand side by side with no gaps.

Find the largest area of a rectangle that fits completely inside the bars. The rectangle covers whole bars that are next to each other, and it can be no taller than the shortest bar it covers.

Input

An integer array heights.

Output

One integer: the area of the largest rectangle.

Example 1
Input: heights = [2, 1, 5, 6, 2, 3]
Output: 10

Bars 5 and 6 (indices 2 and 3) are both at least 5 tall, so a rectangle of height 5 and width 2 fits: area 10. Nothing bigger fits.

Example 2
Input: heights = [2, 4]
Output: 4

Either the single bar 4 (area 4), or both bars at height 2 (area 2 × 2 = 4).

Constraints
  • • 1 ≤ heights.length ≤ 10^5
  • • 0 ≤ heights[i] ≤ 10^4
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    If I pick a block of bars [l … r], how tall can the rectangle over that block be?

  2. 2.

    Every rectangle has a shortest bar inside it. What if I made each bar "the shortest bar" in turn?

  3. 3.

    If bar i is the shortest bar of the rectangle, how far left and right can the rectangle stretch?

  4. 4.

    What stops the rectangle from stretching further?

  5. 5.

    When I read a bar that is shorter than the bars before it, which earlier bars can no longer stretch to the right?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceFix the left edge, extend right, keep the minimum height so farO(N²)O(1)
BetterTwo stack passes find each bar's walls; a third pass computes the areasO(N)O(N)
OptimalPop a bar when a shorter one arrives; the arrival is its right wall, the new top is its left wallO(N)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “largest rectangle”The height of a rectangle is the minimum bar it covers → think about each bar as the minimum.
  • “bars side by side”A contiguous range. Each bar's range is limited by the nearest shorter bar on each side.
  • “up to 10^5 bars”O(n²) is too slow. We need each bar's walls in O(1) amortized → monotonic stack.
Each element as the minimum+stretch to previous smaller and next smaller+value × width→Monotonic Stack (contribution / boundaries)

This is the anchor of the contribution sub-pattern: for each element, find how far it stretches as the minimum. Here we take the max of height × width, so ties don't change the answer. In Sum of Subarray Minimums we add up every element's share, and then the tie rule decides whether a subarray is counted once or twice.

10

Don't make this mistake

Using the popped bar's own index as the left edge
✗ int width = i - j; // j = index that was just popped
✓ int left = st.empty() ? -1 : st.back(); int width = i - left - 1;

Taller bars to the left of j were popped earlier, but they still belong to j's rectangle. The left wall is the bar under j on the stack. In [2, 1, 5, 6, 2, 3], bar 1 is popped at the end with an empty stack: its width is 6 (all bars), not 5.

Wrong width when the stack becomes empty
✗ int width = i - st.back() - 1; // st may be empty now
✓ int width = st.empty() ? i : i - st.back() - 1;

An empty stack after the pop means no bar on the left is shorter. The rectangle reaches index 0, so the width is i. Reading st.back() on an empty vector is undefined behaviour.

Forgetting the final flush
✗ for (int i = 0; i < n; i++) { ... } // no sentinel, no cleanup
✓ for (int i = 0; i <= n; i++) { int h = (i == n) ? 0 : heights[i]; ... }

Bars with no shorter bar to their right are never popped inside the loop. For [1, 2, 3, 4, 5] nothing is popped at all and the answer would be 0 instead of 9. The height-0 bar at i = n pops them with right wall n.

Not knowing why int is safe here

The biggest area is at most n × max height = 10^5 × 10^4 = 10^9, which fits in an int (limit about 2.1·10^9). So int is fine. If the limits were bigger (say heights up to 10^9), you would need long long for height * width.

Using two pointers like Container With Most Water

Pattern-confusion trap. In Container With Most Water the water level depends only on the two end lines, so moving the shorter end inward is safe. Here every bar inside the rectangle limits its height, so the minimum of the whole range matters and moving an end pointer can skip the answer. Use each bar as the minimum and find its walls with a stack.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Monotonic Stack translate into production architectures and low-level memory mechanics.