42. Trapping Rain Water - Explanation
Description
You are given an array of non-negative integers height which represent an elevation map. Each value height[i] represents the height of a bar, which has a width of 1.
Return the total amount of water that can be trapped between the bars.
Example 1:
Input: height = [0,2,0,3,1,0,1,3,2,1]
Output: 9Constraints:
1 <= height.length <= 20,0000 <= height[i] <= 100,000
Topics
Recommended Time & Space Complexity
You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array.
Hint 1
How can we determine the amount of water that can be trapped at a specific position in the array? Perhaps looking at the image might help clarify.
Hint 2
From the image, we can see that to calculate the amount of water trapped at a position, the greater element to the left l and the greater element to the right r of the current position are crucial. The formula for the trapped water at index i is given by: min(height[l], height[r]) - height[i].
Hint 3
A brute force solution would involve iterating through the array with index i, finding the greater elements to the left (l) and right (r) for each index, and then calculating the trapped water for that position. The total amount of trapped water would be the sum of the water trapped at each index. Finding l and r for each index involves repeated work, resulting in an O(n^2) solution. Can you think of a more efficient approach? Maybe there is something that we can precompute and store in arrays.
Hint 4
We can store the prefix maximum in an array by iterating from left to right and the suffix maximum in another array by iterating from right to left. For example, in [1, 5, 2, 3, 4], for the element 3, the prefix maximum is 5, and the suffix maximum is 4. Once these arrays are built, we can iterate through the array with index i and calculate the total water trapped at each position using the formula: min(prefix[i], suffix[i]) - height[i].
Prerequisites
Before attempting this problem, you should be comfortable with:
- Arrays - Basic array traversal and element access
- Prefix and Suffix Arrays - Precomputing running maximums from both directions
- Two Pointers - Using left and right pointers that move toward each other based on conditions
- Stack - Using a monotonic stack to track indices and compute bounded regions
1. Brute Force
Intuition
For each position, the water trapped above it depends on the tallest bar to its left and the tallest bar to its right.
If we know these two values, the water at index i is:
min(leftMax, rightMax) - height[i]
The brute-force method recomputes the left maximum and right maximum for every index by scanning the array each time.
Algorithm
- If the input list is empty, return
0. - Let
nbe the length of the array and initializeres = 0. - For each index
i:- Compute
leftMaxby scanning from index0toi. - Compute
rightMaxby scanning from indexi + 1to the end. - Add
min(leftMax, rightMax) - height[i]tores.
- Compute
- After processing all positions, return
res.
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
n = len(height)
res = 0
for i in range(n):
leftMax = rightMax = height[i]
for j in range(i):
leftMax = max(leftMax, height[j])
for j in range(i + 1, n):
rightMax = max(rightMax, height[j])
res += min(leftMax, rightMax) - height[i]
return resTime & Space Complexity
- Time complexity:
- Space complexity:
2. Prefix & Suffix Arrays
Intuition
Instead of recomputing the tallest bar to the left and right for every index, we can precompute these values once.
We build two arrays:
leftMax[i]= tallest bar from the start up to indexirightMax[i]= tallest bar from the end up to indexi
Once we have these, the trapped water at position i is simply:
min(leftMax[i], rightMax[i]) - height[i]
This removes the repeated work from the brute-force approach and makes the solution more efficient and easier to understand.
Algorithm
- If the array is empty, return
0. - Create two arrays:
leftMaxof sizenrightMaxof sizen
- Fill
leftMax:leftMax[0] = height[0]- For each
ifrom1ton - 1,leftMax[i] = max(leftMax[i - 1], height[i])
- Fill
rightMax:rightMax[n - 1] = height[n - 1]- For each
ifromn - 2down to0,rightMax[i] = max(rightMax[i + 1], height[i])
- Compute trapped water:
- For each index
i, addmin(leftMax[i], rightMax[i]) - height[i]to the result.
- For each index
- Return the total trapped water.
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n == 0:
return 0
leftMax = [0] * n
rightMax = [0] * n
leftMax[0] = height[0]
for i in range(1, n):
leftMax[i] = max(leftMax[i - 1], height[i])
rightMax[n - 1] = height[n - 1]
for i in range(n - 2, -1, -1):
rightMax[i] = max(rightMax[i + 1], height[i])
res = 0
for i in range(n):
res += min(leftMax[i], rightMax[i]) - height[i]
return resTime & Space Complexity
- Time complexity:
- Space complexity:
3. Stack
Intuition
The stack helps us find places where water can collect.
When we see a bar that is taller than the bar on top of the stack, it means we've found a right wall for a container.
The bar we pop is the bottom, and the new top of the stack becomes the left wall.
With a left wall, bottom, and right wall, we can calculate how much water fits in between.
We keep doing this as long as the current bar keeps forming valid containers.
Algorithm
- Create an empty stack and set
res = 0. - Loop through each index
i:- While the stack is not empty and
height[i]is taller than the bar at the stack's top:- Pop the top index — that's the bottom.
- If the stack is not empty:
- Compute the trapped water between the new top (left wall) and the current bar (right wall).
- Add it to
res.
- Push the current index onto the stack.
- While the stack is not empty and
- Return
resafter the loop finishes.
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
stack = []
res = 0
for i in range(len(height)):
while stack and height[i] >= height[stack[-1]]:
mid = height[stack.pop()]
if stack:
right = height[i]
left = height[stack[-1]]
h = min(right, left) - mid
w = i - stack[-1] - 1
res += h * w
stack.append(i)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
4. Two Pointers
Intuition
Water at any position depends on the shorter wall between the left and right sides.
So if the left wall is shorter, the right wall can't help us—water is limited by the left side.
That means we safely move the left pointer inward and calculate how much water can be trapped there.
Similarly, if the right wall is shorter, we move the right pointer left.
As we move the pointers, we keep track of the highest wall seen so far on each side (leftMax and rightMax).
The water at each position is simply:
max wall on that side – height at that position
Algorithm
- Set two pointers:
lat the startrat the end
TrackleftMaxandrightMaxas the tallest walls seen.
- While
l < r:- If
leftMax < rightMax:- Move
lright. - Update
leftMax. - Add
leftMax - height[l]to the result.
- Move
- Else:
- Move
rleft. - Update
rightMax. - Add
rightMax - height[r]to the result.
- Move
- If
- Return the total trapped water.
class Solution:
def trap(self, height: List[int]) -> int:
if not height:
return 0
l, r = 0, len(height) - 1
leftMax, rightMax = height[l], height[r]
res = 0
while l < r:
if leftMax < rightMax:
l += 1
leftMax = max(leftMax, height[l])
res += leftMax - height[l]
else:
r -= 1
rightMax = max(rightMax, height[r])
res += rightMax - height[r]
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Calculating Water at Boundary Bars
The leftmost and rightmost bars can never hold water above them since there's no wall on one side to contain it. Including them in water calculations gives wrong results.
Using Current Bar Height Instead of Max Heights
Water at each position depends on the minimum of the maximum heights to its left and right, minus the current height. Using the current bar's height in the max comparison instead of tracking running maximums is incorrect.
# Wrong: comparing current height directly
water = min(height[l], height[r]) - height[i]
# Correct: use maximum heights seen so far
water = min(leftMax, rightMax) - height[i]Negative Water Values
When the current bar is taller than the limiting wall, the water calculation yields a negative value. This happens at peaks and should contribute zero water, not negative.
# Wrong: can add negative water
res += leftMax - height[i]
# Correct: ensure non-negative
res += max(0, min(leftMax, rightMax) - height[i])Wrong Pointer Movement in Two Pointers
In the two-pointer approach, always move the pointer on the side with the smaller max height. Moving the wrong pointer breaks the invariant that the smaller side determines the water level.
Sign in to join the discussion