Trapping Rain Water Solution
Given n non-negative integers representing an elevation map, compute how much water it can trap after raining.
Problem Statement
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. Water at each position is determined by the minimum of the tallest bars on its left and right, minus its own height.
Brute Force โ O(nยฒ)
For each bar, scan left to find the tallest bar to its left, scan right for the tallest bar to its right. Water at position i = min(leftMax, rightMax) - height[i]. Two linear scans per bar = O(nยฒ).
| Metric | Value |
|---|---|
| Time | O(nยฒ) โ linear scan per position |
| Space | O(1) |
Two Pointers โ O(n) time, O(1) space
Maintain leftMax and rightMax as we move pointers inward from both ends. At each step, process the side with the smaller max โ because that side's water level is determined (the other side is guaranteed to be at least as tall).
- Step 1:
left = 0,right = n-1,leftMax = 0,rightMax = 0. - Step 2: While
left < right: - Step 3: If
height[left] < height[right]: updateleftMax. Water at left =leftMax - height[left].left++. - Step 4: Else: update
rightMax. Water at right =rightMax - height[right].right--.
- If
height[left] < height[right], we know the right side has at leastheight[right]as a wall. - So the water at
leftdepends only onleftMaxโ the right side is guaranteed tall enough. - We don't need to know the exact rightMax. This is the key insight that eliminates the need for prefix arrays.
- Precompute
leftMax[i]= max(height[0..i]) andrightMax[i]= max(height[i..n-1]). - Then water at i =
min(leftMax[i], rightMax[i]) - height[i]. - This is O(n) time and O(n) space โ good for understanding, but the two-pointer approach eliminates the arrays.
Visual Walkthrough
Trace through height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]:
Code โ Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Brute Force (per bar) | O(nยฒ) | O(1) | Re-scans for max at each bar |
| Prefix/Suffix Max Arrays | O(n) | O(n) | Two extra arrays; clearer logic |
| Monotonic Stack | O(n) | O(n) | Row-by-row water; more complex to implement |
| Two Pointers โ optimal | O(n) | O(1) | Process shorter side; running max replaces arrays |
- If the two-pointer approach feels unintuitive, first implement the prefix-max/suffix-max version.
- Once you see that you only ever need
leftMax[i]andrightMax[i]at each position, you realize two running variables suffice โ and the two-pointer technique emerges naturally.
Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| Flat | [3, 3, 3] | No valleys โ water = 0 everywhere. |
| Ascending | [1, 2, 3, 4] | No valleys โ water slides off to the left. |
| Descending | [4, 3, 2, 1] | No valleys โ water slides off to the right. |
| Single valley | [3, 0, 3] | Water = min(3,3) - 0 = 3 at index 1. |
| All zeros | [0, 0, 0] | No walls to hold water โ 0. |
| Single bar | [5] | No pair of walls โ 0. Pointers cross immediately. |
leftMax after computing water instead of before. The correct order is: update max first (leftMax = max(leftMax, height[left])), then compute water (leftMax - height[left]). If the current bar is taller than leftMax, it becomes the new max and traps 0 water โ not a negative amount.