AlgoMaster Logo

Trapping Rain Water

hardFrequency9 min readUpdated June 23, 2026

Understanding the Problem

The elevation map is a cross-section of terrain. When it rains, water fills the valleys between the bars. The question is how many units of water are trapped in total.

The cleanest way to count it is per bar rather than per pool. For any single bar at index i, the water sitting above it is bounded by the tallest bar to its left and the tallest bar to its right. Water rises only to the level of the shorter of those two walls, since it would spill over the shorter side, and then the bar's own height is subtracted.

So the water at position i is water[i] = min(maxLeft, maxRight) - height[i], where maxLeft is the tallest bar from index 0 to i and maxRight is the tallest bar from index i to n-1. If that value is negative (the bar is taller than the surrounding walls), no water sits above it and the contribution is 0.

Every approach below is a different way to compute maxLeft and maxRight efficiently, except the last one, which counts water in horizontal layers instead.

Key Constraints:

  • 1 <= n <= 2 * 10^4 → With up to 20,000 elements, an O(n^2) scan is around 4 x 10^8 operations. That is risky under a tight time limit, so an O(n) solution is the target.
  • 0 <= height[i] <= 10^5 → Heights are non-negative, and the total trapped water is bounded by n * max(height), about 2 x 10^9. That exceeds the signed 32-bit range only in the worst case, but for these constraints the LeetCode answer fits in a 32-bit int, so the return type of int is safe.

Approach 1: Brute Force

Intuition

For each bar, scan left and right to find the tallest wall on each side, then the water above that bar is min(maxLeft, maxRight) - height[i]. This applies the formula from above directly: for every index, scan the whole array to its left and to its right to find the two maximums.

Algorithm

  1. Initialize totalWater = 0
  2. For each index i from 0 to n-1:
    • Scan left from i to 0 to find maxLeft (tallest bar at or before i)
    • Scan right from i to n-1 to find maxRight (tallest bar at or after i)
    • Water at index i = min(maxLeft, maxRight) - height[i]
    • Add this to totalWater
  3. Return totalWater

Example Walkthrough

1i=0: maxL=0, maxR=3. min(0,3)-0 = 0. water=0
0
0
i
1
1
2
0
3
2
4
1
5
0
6
1
7
3
8
2
9
1
10
2
11
1
1/7

Code

Recomputing the left and right maximums from scratch for every bar repeats the same work. The next approach precomputes both in a single pass each.

Approach 2: Prefix Max Arrays

Intuition

maxLeft[i] depends only on maxLeft[i-1] and height[i]: it is the larger of the two. The same recurrence runs in the other direction for maxRight. So both arrays can be built in a single pass each, after which the water at every index follows from a third pass. This is the prefix-and-suffix-maximum pattern: precompute the running maximums once, then combine them.

Algorithm

  1. Build leftMax array: leftMax[i] = max(leftMax[i-1], height[i]), scanning left to right
  2. Build rightMax array: rightMax[i] = max(rightMax[i+1], height[i]), scanning right to left
  3. For each index i, water = min(leftMax[i], rightMax[i]) - height[i]
  4. Sum all water values and return

Example Walkthrough

1Step 1: Build leftMax array (scan left to right)
0
0
1
1
2
0
3
2
4
1
5
0
6
1
7
3
8
2
9
1
10
2
11
1
1/7

Code

The prefix max approach reaches O(n) time but spends O(n) extra space on two arrays. The next approach removes those arrays and computes the water in constant space using two pointers.

Approach 3: Two Pointers (Optimal)

Intuition

The water at a position is min(maxLeft, maxRight) - height[i], so only the smaller of the two maximums matters. The larger one never affects the result. That observation removes the need to store both maximum arrays.

Use two pointers, left and right, starting at the ends and moving inward, along with leftMax (the tallest bar seen from the left so far) and rightMax (the tallest bar seen from the right so far). Compare height[left] and height[right] to decide which side to process.

When height[left] < height[right], the left pointer's water depends only on leftMax. The water at left is min(leftMax, trueRightMax) - height[left], where trueRightMax is the actual tallest bar to the right of left. We never have to compute trueRightMax: the bar at right is itself to the right of left, so trueRightMax is at least height[right], which exceeds height[left]. Whenever water sits on left, the binding wall is leftMax (a value at least height[left]), and the right side already offers a wall taller than height[left], so the water level is set by leftMax. The water at left can be computed from leftMax alone, and left advances.

The symmetric case, height[right] <= height[left], constrains the right pointer's water by rightMax.

Algorithm

  1. Initialize left = 0, right = n - 1, leftMax = 0, rightMax = 0, totalWater = 0
  2. While left < right:
    • If height[left] < height[right]:
      • If height[left] >= leftMax, update leftMax = height[left]
      • Else, add leftMax - height[left] to totalWater
      • Move left one step right
    • Else:
      • If height[right] >= rightMax, update rightMax = height[right]
      • Else, add rightMax - height[right] to totalWater
      • Move right one step left
  3. Return totalWater

Example Walkthrough

1Init: L=0, R=11, leftMax=0, rightMax=0, water=0
0
L
0
1
1
2
0
3
2
4
1
5
0
6
1
7
3
8
2
9
1
10
2
11
1
R
1/12

Code

Approach 4: Monotonic Stack

Intuition

The previous approaches count water vertically, one column at a time. A stack counts it horizontally instead, filling each trapped region as a flat slab between a left wall and a right wall.

Scan left to right and keep a stack of bar indices whose heights are non-increasing from bottom to top. When the current bar is taller than the bar on top of the stack, that top bar is a local dip with a wall on both sides: the new top of the stack on its left, and the current bar on its right. Pop the dip, and the water resting on it is a rectangle whose height is min(leftWall, rightWall) - dipHeight and whose width is the gap between the two walls. Add that slab and keep popping while the current bar stays taller than the new top.

This bounds each region by the shorter of its two walls, the same rule as before, but resolves a whole horizontal layer per pop rather than a single column.

Algorithm

  1. Initialize an empty stack of indices and totalWater = 0.
  2. For each index i from 0 to n-1:
    • While the stack is non-empty and height[i] > height[stack.top()]:
      • Pop the top index as bottom (the dip being filled).
      • If the stack is now empty, there is no left wall, so break.
      • Let left = stack.top(). The bounded height is min(height[left], height[i]) - height[bottom].
      • The width is i - left - 1.
      • Add boundedHeight * width to totalWater.
    • Push i onto the stack.
  3. Return totalWater.

Example Walkthrough

1i=0..2: push dips. stack=[1,2] (indices). water=0
0
0
1
1
2
0
3
2
4
1
5
0
6
1
7
3
8
2
9
1
10
2
11
1
1/9

Code

The stack matches the two-pointer approach on time but uses O(n) space, so the two-pointer solution remains the best on space. The horizontal-slab technique it uses carries over to related problems where counting columns directly is harder to set up.