Trapping Rain Water
Given n non-negative integers where height[i] is the height of a bar of width 1 at position i, imagine it rains until no more water can settle. Return the total units of water trapped in the dips between the bars. Water sits above a bar only if there are taller bars on both sides to hold it in; water at the outer edges spills away. Return a single number (0 if nothing is trapped).
- n == height.length
- 1 <= n <= 2 × 10⁴
- 0 <= height[i] <= 10⁵
height = [0,1,0,2,1,0,1,3,2,1,2,1]6height = [4,2,0,3,2,5]9height = [3,2,1]0Picture a row of bars of different heights, then rain. Water pools in the dips. The one fact everything rests on: the water sitting on top of a single bar rises only as high as the shorter of two walls — the tallest bar somewhere to its left, and the tallest bar somewhere to its right. Call those leftMax and rightMax. The water above bar i is min(leftMax, rightMax) - height[i] (and zero if that comes out negative). Add it up over every bar and you have the answer. The whole challenge is computing those two "tallest wall on each side" values without wasting time or memory.
The literal approach: for each bar, scan all the way left for its tallest left wall and all the way right for its tallest right wall, then add its water. Correct, but each bar triggers two full scans — O(N²).
total = 0
for i in range(len(height)):
left_max = max(height[:i+1]) # tallest wall at or left of i
right_max = max(height[i:]) # tallest wall at or right of i
total += min(left_max, right_max) - height[i]
return totalAn easy speedup is to precompute those walls once. Sweep left to right filling a leftMax array, sweep right to left filling a rightMax array, then read both off for each bar. That is O(N) time — but it spends O(N) extra memory on the two arrays. Can we get the walls without storing them?
Yes — with the converging walk, plus one running number on each side. Put left at the first bar and right at the last. As left moves in it remembers leftMax, the tallest bar it has passed so far; as right moves in it remembers rightMax. At each step, advance the pointer sitting on the shorter current bar, and bank that bar's water using the running max on its own side.
left, right = 0, len(height) - 1
left_max, right_max, water = 0, 0, 0
while left < right:
if height[left] < height[right]:
left_max = max(left_max, height[left])
water += left_max - height[left] # water on the left bar
left += 1
else:
right_max = max(right_max, height[right])
water += right_max - height[right] # water on the right bar
right -= 1
return waterThis is the subtle step, so let's argue it honestly. Suppose height[left] < height[right], and we're about to settle the left bar using only left_max — ignoring whatever the true tallest wall on its right might be. Is that valid? The water on the left bar is min(left_max, trueRightMax) - height[left]. We're claiming it equals left_max - height[left], which needs left_max <= trueRightMax. Two things guarantee it. First, trueRightMax is at least height[right], since right is one of the bars on that side. Second — the important part — every time left advanced past a tall bar in the past, it did so only because the bar at right was even taller at that moment (that is the exact condition for moving left), and right_max only grows as right comes in. So by the time left_max climbed to some height, the right side had already locked in a wall at least that tall. In short: whenever the left bar is the shorter of the two, its left wall is guaranteed not to exceed the right side's tallest wall, so left_max alone decides the water. The symmetric statement covers the other branch. That is why one number per side is enough — no lookahead required.
left=0 (0), right=3 (2). Left bar shorter. left_max = 0. Water here: 0 − 0 = 0. left→1.left=1 (1), right=3 (2). Left bar shorter. left_max = 1. Water: 1 − 1 = 0. left→2.left=2 (0), right=3 (2). Left bar shorter. left_max stays 1. Water: 1 − 0 = 1. left→3.left meets right, stop. Total trapped: 1 (the dip at position 2, held by the 1 on its left and the 2 on its right).The template again: (a) we read the two end bars and compare them; (b) we move the shorter side; (c) we record water using that side's running max. Like Container With Most Water, the enabling property is not sorting — it is that the quantity at each position is bottlenecked by the smaller of two boundary walls. That bottleneck is what lets us commit the shorter side early and forget it. When you meet a problem about levels, areas, or capacities pinned between a left boundary and a right boundary, that "limited by the weaker side" shape is your cue that converging from both ends — sometimes with a running max in hand — will collapse an O(N²) or O(N)-space solution down to a single O(1)-space pass.
Trapping Rain Water
Bottleneck Rule: Smaller max height dictates water level
Key Insight
We don't need to know the full max height on both sides. If leftMax < rightMax, we know water is limited by leftMax regardless of what's between left and right.
Optimal Approach
Move the pointer with the smaller max height inward. Update the max height for that side, and calculate trapped water based on that max height.