Trapping Rain Water — Two-Pointer in O(n)
Solve Trapping Rain Water in linear time and constant space using the two-pointer technique. Brute force, optimal walkthrough, and interview talk track.
What you'll learn
- ✓Why each bar traps water equal to min(maxLeft, maxRight) minus its height
- ✓How to derive the two-pointer invariant from prefix and suffix max arrays
- ✓A clean O(n) time, O(1) space solution
- ✓How to defend the approach in an interview
- ✓Common edge cases that trip people up
Prerequisites
- •Comfort with the [two-pointer technique](/blog/two-pointers-technique)
- •A working sense of [Big-O notation](/blog/big-o-notation-explained)
Trapping Rain Water looks intimidating, but it collapses to one idea: each column traps min(maxLeft, maxRight) - height[i] units of water. Lock that in and the two-pointer solution writes itself.
The Problem
Given a non-negative integer array height where each element represents a unit-width bar, compute how much water it can trap after raining.
Example:
- Input:
height = [0,1,0,2,1,0,1,3,2,1,2,1] - Output:
6
The water sits in valleys bounded by taller bars on both sides.
Intuition
For every index, water trapped equals min(maxLeft, maxRight) - height[i]. The brute force scans left and right at every index — O(n^2). A prefix/suffix max array brings it down to O(n) time and O(n) space.
The two-pointer trick gets O(1) space. Track left_max and right_max as you walk inward from both ends. The pointer with the smaller current bar moves; the smaller side is the bottleneck, so the water level at that pointer is fully determined no matter what is on the other side. That invariant — bottleneck side is locked — is the whole insight.
Explanation with Example
Take height = [0,1,0,2,1,0,1,3,2,1,2,1].
- Start
left=0,right=11.height[left]=0 < height[right]=1, so process left.left_maxbecomes 0, no water added, advance. - At
left=2(height 0),left_max=1, add1-0=1unit. - At
left=5(height 0),left_max=2, add2-0=2. - At
left=6(height 1), add2-1=1. - From the right side, when
rightlands on heights belowright_max=3, additional units accumulate.
Total trapped = 6.
height 0 1 0 2 1 0 1 3 2 1 2 1
#
. . . . . . . ## . . .
. . . # ~ ~ ~ ## # ~ #
. # ~ ## # ~ ## ## ## #
l -> <- r
at i=2: min(1, 3) - 0 = 1 unit
at i=5: min(2, 3) - 0 = 2 units Code
def trap(height):
left, right = 0, len(height) - 1
left_max = right_max = 0
total = 0
while left < right:
if height[left] < height[right]:
if height[left] >= left_max:
left_max = height[left]
else:
total += left_max - height[left]
left += 1
else:
if height[right] >= right_max:
right_max = height[right]
else:
total += right_max - height[right]
right -= 1
return totalclass Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0, total = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) leftMax = height[left];
else total += leftMax - height[left];
left++;
} else {
if (height[right] >= rightMax) rightMax = height[right];
else total += rightMax - height[right];
right--;
}
}
return total;
}
}class Solution {
public:
int trap(vector<int>& height) {
int left = 0, right = (int)height.size() - 1;
int leftMax = 0, rightMax = 0, total = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax) leftMax = height[left];
else total += leftMax - height[left];
left++;
} else {
if (height[right] >= rightMax) rightMax = height[right];
else total += rightMax - height[right];
right--;
}
}
return total;
}
};Edge Cases
- Empty array or length less than 3: return 0; nothing can hold water.
- Strictly increasing or decreasing arrays: no valleys, answer is 0.
- A flat plateau like
[3,3,3]: still 0. - Single-cell valleys like
[3,0,3]: traps 3 units. - Large bars at the ends only, e.g.,
[5,0,0,0,5]: traps 15 units.
Complexity Analysis
- Brute force: O(n^2) time, O(1) space.
- Prefix/suffix max arrays: O(n) time, O(n) space.
- Two pointer: O(n) time, O(1) space.
The two-pointer version is the gold standard interviewers want to hear. See Big-O notation for a refresher on why constant factors matter here.
How to Explain It in an Interview
Open with the invariant: water at index i equals min(maxLeft[i], maxRight[i]) - height[i]. Mention the O(n) prefix array solution first to show you understand the principle. Then optimize: if height[left] < height[right], the left side is the bottleneck no matter what is between them, so left_max - height[left] is final. Move the smaller pointer inward and repeat. Emphasize the invariant clearly; interviewers care more about the reasoning than the code.
Related Problems
- Container With Most Water (another classic two-pointer drill)
- Largest Rectangle in Histogram (monotonic stack)
- Trapping Rain Water II (2D version, uses a heap)
- Product of Array Except Self (similar prefix/suffix idea)
Wrap up
This problem rewards recognizing one invariant and then squeezing it into O(1) space. Once you internalize the min(leftMax, rightMax) formula, the two-pointer move is mechanical.
Related articles
- DSA 3Sum — Sort and Two Pointers, Step by Step
A careful walkthrough of 3Sum using sort plus two pointers. We handle duplicate triplets cleanly and avoid the classic off-by-one traps.
- DSA Container With Most Water — Two-Pointer Walkthrough
Solve Container With Most Water with the two-pointer technique. We prove correctness and contrast it with the quadratic brute force.
- DSA Reverse String — Two-Pointer In-Place Solution
Solve the Reverse String problem in place using the two-pointer technique. Walkthrough, complexity analysis, edge cases, and clean Java, Python, and C++ implementations.
- DSA Binary Tree Level Order Traversal — Queue Size Snapshot
The queue-with-size BFS pattern, why DFS still works, and how this template extends to zigzag and right-side view.