Algorithm

Trapping Rain Water

Two Pointer Pattern

Trapping Rain Water

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it can trap after raining.

CONSTRAINTS
  • n == height.length
  • 1 <= n <= 2 * 10^4
  • 0 <= height[i] <= 10⁵
EXAMPLE 1
Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Water settles in each dip up to the shorter of the tallest walls on its two sides; summed across all bars it comes to 6 units.
EXAMPLE 2
Input: height = [4,2,0,3,2,5]
Output: 9
The valley between the 4 on the left and the 5 on the right holds water up to height 4 where the floor allows, totaling 9 units.
EXAMPLE 3
Input: height = [3,2,1]
Output: 0
The bars only descend, so there is never a taller wall on the right to hold water in. Nothing is trapped.
Can water sit at the two outer edges?
No. The outermost bars have no wall beyond them, so any water there just spills off the ends.
What does the height of a bar represent?
Each bar has width 1 and the given height; water fills the empty space above bars that sit lower than the walls on both sides.
Does the input need to be sorted?
No. This works on the raw elevation map — the discard rule comes from the shorter boundary wall, not from any ordering.
What should a strictly increasing or decreasing array return?
Zero. With bars only going up (or only down), one side never has a taller wall to trap water against.

Trapping rain water is fundamentally a challenge of determining the water level above each elevation bar. The water level at any index is limited by the tallest wall to its left and the tallest wall to its right. Specifically, the amount of water at a given point is the minimum of these two peaks, minus the height of the bar itself.

The Exhaustive Search (O(N²))

The most straightforward way to solve this is to iterate through every single bar in the array and, for each one, perform a separate scan to the left and to the right to find the maximum height in both directions. While easy to understand, this repeated scanning leads to an O(N²) time complexity, which is far too slow as the number of bars increases.

python
# Brute force: scan left and right for every bar
def brute_force(height):
    total = 0
    for i in range(len(height)):
        left_max = max(height[:i+1])
        right_max = max(height[i:])
        total += min(left_max, right_max) - height[i]
    return total

Instead of re-scanning, we can precompute the tallest walls to the left and right for each bar. By creating two arrays to store these maximums, we reduce the time complexity to O(N).

python
# Intermediate: Dynamic Programming with extra space
def trap_dp(height):
    n = len(height)
    left_max = [0] * n
    right_max = [0] * n
    
    # Precompute left and right maxes
    for i in range(1, n): left_max[i] = max(left_max[i-1], height[i-1])
    for i in range(n-2, -1, -1): right_max[i] = max(right_max[i+1], height[i+1])
    
    water = 0
    for i in range(n):
        level = min(left_max[i], right_max[i])
        if level > height[i]:
            water += level - height[i]
    return water

While this is much faster, it requires O(N) extra space for those auxiliary arrays. We can do even better, though, by using two pointers to calculate these maximums on the fly, eliminating the need for extra memory entirely.

The Two-Pointer Insight

The bottleneck is always the shorter of the two boundary walls. We don't need to know the absolute tallest wall in the entire array—we only need to know that there exists some wall on the left and some wall on the right that are taller than our current position. By using two pointers starting from both ends, we can maintain the running maximums (leftMax and rightMax) on the fly, eliminating the need to look ahead or behind.

Optimal Strategy: Boundary Convergence

We use two pointers, l and r. We keep track of l_max and r_max. At each step, we compare the heights of the walls at the two pointers. Because we know the shorter side is limited by its own current max, we can safely calculate the water trapped at the shorter pointer and advance it inward, knowing that the water level is constrained by the maximum wall we have already seen on that side.

python
# Optimal: two pointers
def trap(height):
    l, r = 0, len(height) - 1
    l_max, r_max, water = 0, 0, 0
    while l < r:
        if height[l] < height[r]:
            l_max = max(l_max, height[l])
            water += l_max - height[l]
            l += 1
        else:
            r_max = max(r_max, height[r])
            water += r_max - height[r]
            r -= 1
    return water
Worked Example:[0, 1, 0, 2]
0
0
L
1
1
2
0
3
2
R
We initialize Left at index 0 and Right at index 3. l_max and r_max are both 0. Since height[L] < height[R], we update l_max and move Left.
0
0
1
1
L
2
0
3
2
R
l_max is updated to 1. Since height[L] (1) < height[R] (2), we update l_max and advance Left to index 2.
0
0
1
1
2
0
L
3
2
R
At index 2, height is 0. Since l_max is 1, we trap 1 - 0 = 1 unit of water. Left advances to index 3.
0
0
1
1
2
0
3
2
Left meets Right at index 3, terminating the traversal. The total trapped water is 1 unit.
Interactive Strategy Visualization

Trapping Rain Water

Bottleneck Rule: Smaller max height dictates water level

L
0
1
2
3
4
5
6
7
8
9
10
R
11
Left Max
0
Right Max
0
Total Trapped
0
The journey begins! We place our scouts at both ends of this rugged terrain. Initial peak levels: 0 on both sides.
O(N²) Brute Force
O(N) Time · O(N) Space Prefix Maxima
O(N) Time · O(1) Space Converging Scan