Trapping Rain Water looks like a geometry puzzle but is really a two-pointer classic. The trick is realizing that the water above any bar depends only on the tallest walls to its left and right — and you can discover those walls without ever scanning twice.
Problem. Given height, a list of non-negative integers where each value is the height of a bar of
width 1, compute how many units of rain water can be trapped between the bars after it rains.
Example: height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] → 6 units trapped.
The slow way first
For each bar, the water sitting on top of it is min(tallest wall to the left, tallest wall to the right) − its own height (never less than zero). The obvious solution scans left and right from every bar to find those two walls:
That is O(n²) — for each of the n bars we do an O(n) scan. Correct, but wasteful, because we recompute the same maxima over and over.
The idea: move the shorter wall
Put a pointer l at the left end and r at the right, and track left_max (tallest bar seen from the left so far) and right_max (tallest from the right). The insight:
If height[l] <= height[r], then whatever sits to the right, the right wall is at least as tall as the bar at l. So the water above bar l is decided entirely by left_max — we can settle it right now and move l inward. Otherwise the symmetric argument settles bar r.
Because we always advance the shorter side, the taller side's max is a guaranteed upper bound — so the shorter side's own running max is the true water level there.
Walk through it
Step through the animation. Pointers l and r start at the ends. At each step we compare the two end bars and work the shorter one: update its running max, add max − height to the total, and slide that pointer in. Watch the dips behind a wall fill up (+1, +2, +1) and the running maxes climb. When l and r meet, every bar has been settled exactly once and the total is 6.
Pseudocode
l = 0, r = last index
left_max = right_max = 0
water = 0
while l < r:
if height[l] <= height[r]:
left_max = max(left_max, height[l])
water += left_max - height[l]
l += 1
else:
right_max = max(right_max, height[r])
water += right_max - height[r]
r -= 1
return waterThe Python solution
def trap(height):
l, r = 0, len(height) - 1
left_max = right_max = 0
water = 0
while l < r:
if height[l] <= height[r]:
left_max = max(left_max, height[l])
water += left_max - height[l]
l += 1
else:
right_max = max(right_max, height[r])
water += right_max - height[r]
r -= 1
return water- The
while l < rloop runs until the pointers meet — each iteration finalizes exactly one bar. if height[l] <= height[r]picks the shorter end; that side's water is safe to settle now.left_max = max(left_max, height[l])raises the left wall, thenwater += left_max - height[l]adds the gap (zero when the current bar is the wall).- The
elsebranch is the mirror image for the right side. left_maxandright_maxare plain integers, so we never allocate an array — O(1) extra space.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (scan both sides per bar) | O(n²) (slow) | rescans walls every time |
| Prefix-max arrays | O(n) (moderate) | but O(n) extra space |
| Two pointers (this solution) | O(n) (moderate) | one pass, two integers |
O(1) (fast)The two-pointer version matches the speed of the prefix-array approach while dropping its memory down to a couple of integers.
When this pattern shows up
Reach for two pointers from both ends whenever a value at one position depends on a max/min from both directions and you can argue that the smaller (or larger) side is safe to commit first. Container With Most Water uses the exact same move.
Always move the shorter side. If you advance the taller pointer, you can no longer guarantee the
other side bounds the water, and you will undercount. Also clamp the contribution to zero implicitly:
because you only add after updating the running max, max − height is never negative.
Practice
At a step where height[l]=0, height[r]=2, and left_max is already 2, how much water does this bar add and which pointer moves?
1. Why do we always advance the pointer on the shorter side?
2. What water sits on top of a single bar in this problem?
3. What is the extra space used by the two-pointer solution?
4. Why can the term max − height never be negative here?