AlgoMaster Logo

Trapping Rain Water

Last Updated: March 21, 2026

hard
4 min read

Understanding the Problem

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

The key insight is to think about water on a per-bar basis rather than trying to figure out entire pools at once. For any single bar at index i, the water above it is determined by the tallest bars to its left and right. Water rises to the level of the shorter of those two tallest bars (because it would spill over the shorter side), and then you subtract the bar's own height.

So the formula for 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 this value is negative (the bar is taller than the surrounding walls), no water sits above it, so the contribution is 0.

Every approach to this problem is just a different way of computing maxLeft and maxRight efficiently.

Key Constraints:

  • 1 <= n <= 2 * 10^4 → With up to 20,000 elements, O(n^2) means 4 \* 10^8 operations, which is borderline. An O(n) solution is preferred, but O(n^2) might squeak by with simple operations.
  • 0 <= height[i] <= 10^5 → All values are non-negative, so we don't need to worry about negative heights. The sum of trapped water fits comfortably in a 32-bit integer.

Approach 1: Brute Force

Intuition

The most direct approach: for each bar, look left and right to find the tallest walls on each side. The water above that bar is min(maxLeft, maxRight) - height[i].

This is essentially computing the formula from the "Understanding the Problem" section in the most literal way possible. For each index, we scan the entire array to the left and right to find the 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

The brute force recomputes the left and right maximums from scratch for every bar, which is redundant. What if we precomputed those maximums in a single pass each?

Approach 2: Prefix Max Arrays

Intuition

The brute force recomputes maxLeft and maxRight from scratch for every index. But notice that maxLeft[i] only depends on maxLeft[i-1] and height[i]. The same goes for maxRight from the other direction. So we can build both arrays in a single pass each, then compute the water in a third pass.

This is a classic dynamic programming technique: precompute prefix and suffix maximums, 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 gets us to O(n) time, but we're using O(n) extra space for two arrays. Can we compute the water without storing all the prefix maximums, using only constant space?

Approach 3: Two Pointers (Optimal)

Intuition

Here's the clever observation that makes O(1) space possible: at any position, the water is determined by min(maxLeft, maxRight) - height[i]. We don't actually need to know both max values precisely. We only need to know the smaller one, because that's the bottleneck.

Imagine two pointers, one at each end of the array, moving inward. We also track leftMax (the tallest bar seen from the left so far) and rightMax (the tallest bar seen from the right so far).

If leftMax < rightMax, we know for sure that the water at the left pointer is constrained by leftMax, regardless of what rightMax actually is. Why? Because the true right max for the left pointer is at least rightMax (there could be taller bars between the right pointer and the left pointer), so min(leftMax, trueRightMax) = leftMax. We can safely compute the water at the left pointer and advance it.

The same logic applies symmetrically: if rightMax <= leftMax, the water at the right pointer is constrained 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