advanced8 min read·Updated September 20, 2026

Trapping Rain Water Explained: Tracing [0,1,0,2,1,0,1,3,2,1,2,1]

Master the two-pointer trap with our elevation valley mental model. Walk through the classic [0,1,0,2,1,0,1,3,2,1,2,1] example step by step.

By Learnisim AI·Published September 20, 2026
LEARNING ARCSetup → Model in the example → Full trace → Twist → Edge cases → Apply
PREREQUISITES
  • Basic array manipulation
  • Two-pointer technique fundamentals
Trapping Rain Water: Two-Pointer & Elevation Valley Model height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] | Total Trapped Water = 6 units Elevation Map & Water Levels (Bar-by-Bar Grid) 0 1 +1 2 3 +1 4 +2 5 +1 6 7 8 +1 9 10 11 Left Pointer (L = 0) Right Pointer (R = 11) 1. Two-Wall Boundary Rule • Water at index i depends on min(leftMax, rightMax). • Water[i] = max(0, min(L_max, R_max) - height[i]) Bounded by the shorter peak! 2. Two-Pointer Sweep • Initialize L=0, R=n-1 • If height[L] < height[R]: update leftMax or add water, then L++ • Else do symmetric for R-- 3. Complexity & Result • Time Complexity: O(n) • Space Complexity: O(1) Ans: 6 Units Trapped No nested scans required!
Water trapped by [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] overview diagram
Why

Why We Need a Smarter Way to Measure Trapped Rain Water

Phase 1: The Pooling Problem

Imagine looking at a cross-section of a rugged landscape after a heavy storm. Rain has fallen, and water has pooled in the depressions between uneven hills and ridges. Given the elevation map height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], how many total units of water remain trapped between those vertical barriers instead of spilling off the edges?
If you inspect bar by bar, water does not depend merely on how tall the immediate neighbors are. A single drop of water at index 2 (with height 0) is bounded by the tall wall at index 1 (height
1.to its left and an even taller wall at index 3 (height
2.to its right. It can rise as high as the shorter of those two bounding walls.
Without a systematic mental model, calculating this feels overwhelming: you might try scanning left and right infinitely from every single index, or getting tripped up by valleys nested inside larger valleys. We need a way to look at height constraints globally without re-scanning the entire array at every single step.
python
height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
# Think about why index 2 (height 0) can trap water, 
# while index 0 (height 0) cannot.
Trapping Rain Water: Global Height Constraints (height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) 3 2 1 0 0 1 2 3 4 5 6 7 8 9 10 11 Water at index 2 = 1 min(LeftMax, RightMax) - h The Global Rule Water[i] = min( MaxLeft, MaxRight ) - h[i] ⚠️ Why Naive Scanning Fails: • O(N²) repeated scans waste time • Valleys inside valleys cause confusion ✨ The Two-Pointer Solution: • Track left_max and right_max • Process outward-in in O(N) time 1. Boundary Dependency Water level is strictly bounded by the shorter of left/right max walls. 2. Overcoming Complexity Avoid nested valley re-scans using running max bounds dynamically. 3. Total Sum Result Accumulate trapped units across all 12 indices = 6
Why We Need a Smarter Way to Measure Trapped Rain Water diagram
Model

Building the Elevation Valley Mental Model

Phase 2: The Two-Wall Boundary Rule

To understand how water gets trapped in our elevation map height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], we have to stop looking at single bars and start looking at boundaries. Imagine pouring water over this entire terrain: any individual bar at index can only hold water if there is a taller bar somewhere to its left and another taller bar somewhere to its right.
Water does not care about the immediate neighbor alone; it cares about the absolute tallest peaks flanking it on either side. Specifically, the water level sitting directly on top of bar is strictly limited by the height of the shorter of those two bounding peaks. If we define as the maximum height from the start up to index , and as the maximum height from index to the end, the trapped water at any single position is given by:
Looking at our elevation map, index 2 has a height of 0. Its highest obstacle to the left is 1, and its highest obstacle to the right is 3. The limiting boundary is , meaning index 2 can pool water up to height 1. Subtracting its own height of 0 leaves 1 unit of trapped water.
Trapping Rain Water: The Two-Wall Boundary Rule elevation = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] — Water[i] = min(leftMax, rightMax) - height[i] 0 1 2* 3 4 5 6 7 8 9 10 11 Water = 1 unit The Boundary Principle 1. Find LeftMax[i] Tallest peak to the left of bar i. For index 2: max(0..2) = height[1] = 1 2. Find RightMax[i] Tallest peak to the right of bar i. For index 2: max(2..11) = height[7] = 3 3. Compute Trapped Water water = min(leftMax, rightMax) - height For index 2: min(1, 3) - 0 = 1 unit Independent of immediate neighbors! Solid bars = Terrain elevation Cyan translucent = Trapped water Yellow line = LeftMax boundary Pink line = RightMax boundary
Building the Elevation Valley Mental Model diagram
Worked example

Tracing the Two-Pointer Sweep on the Elevation Map

Phase 3: Walking the Elevation Array

Let us trace the two-pointer algorithm step by step on our elevation map height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]. We maintain left pointer , right pointer , leftMax = 0, rightMax = 0, and ans = 0.

Given

Try this: Trace steps:
1.Init: L=0, R=11, leftMax=0, rightMax=0, ans=0.
2.height[L]=0, height[R]=1. leftMax < rightMax -> L moves to 1, leftMax becomes max(0, height[1]) = 1.
3.height[L]=1, height[R]=1. leftMax <= rightMax -> L moves to 2, leftMax remains 1.
4.height[L]=0, height[R]=1. leftMax=1. Water added at index 2 = leftMax - height[2] = 1 - 0 = 1. ans becomes 1.
Phase 3: Tracing the Two-Pointer Sweep (Steps 0–4) 1 1 2 3 4 5 6 7 8 9 10 11 0 L (idx 2) R (idx 11) Left Pointer (L) L = 2 | height[2] = 0 Right Pointer (R) R = 11 | height[11] = 1 Algorithm State leftMax = 1 | rightMax = 1 ans (Total Water) = 1 Trace Step 4 Result: • Since leftMax <= rightMax, L advanced from 0 to 2. • Water added at index 2 = leftMax - height[2] = 1 - 0 = 1. • Total ans increments from 0 to 1.
Tracing the Two-Pointer Sweep on the Elevation Map diagram
Practice

Predicting the Sweep on a Modified Elevation Map

Phase 4: Practice

Now that you have seen how the two-pointer sweep maintains running maximums and accumulates water across [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], it is time to apply the exact same mechanical trace to a slight variation of the array.
Take the original elevation map and alter its center valley by raising the lowest interior bars. Consider the modified elevation map height array: [0, 1, 0, 2, 1, 2, 1, 3, 2, 1, 2, 1]. Notice that the bar at index 5, which was previously 0, is now 2.
Work through the two-pointer initialization: set left pointer L = 0, right pointer R = 11, leftMax = 0, rightMax = 0, and ans = 0. As L and R step inward, compare how the new height of 2 at index 5 alters the boundary conditions compared to the original trace.
python
height = [0, 1, 0, 2, 1, 2, 1, 3, 2, 1, 2, 1]
# Trace L=0, R=11, leftMax=0, rightMax=0, ans=0
# What is the final value of ans?
Apply

Applying the Bounded-Max Pattern to Adjacent Architectural Problems

Phase 5: Applying the Bounded-Max Pattern

The two-pointer elevation sweep we used to solve the array is not just an isolated algorithmic trick—it is a blueprint for any problem where a local resource is constrained by the minimum of two global extremes. Recall that at every step of our trace, the water height above column was strictly decided by . This exact boundary-governed pattern reappears in geometry, skyline layout, and financial reservoir simulations where capacity is limited by the lowest threshold of surrounding walls.
To see this transfer in action, consider a 2D container wall optimization task: given a set of vertical panels of varying heights, you must find two panels that, together with the x-axis, form a container holding the maximum possible water volume (the classic Container With Most Water problem). While that problem uses a greedy two-pointer reduction moving inward from the absolute edges based on height comparison, the underlying mental model remains identical to our rain water trace. You are constantly letting the shorter boundary dictate the active constraint, sacrificing the limiting side in hopes of finding a taller bottleneck.
When encountering a new spatial capacity puzzle, do not immediately jump to brute-force nested loops that check every pair of boundaries. Instead, ask three diagnostic questions:
1. Is the capacity at position determined by the nearest enclosing peaks, or by the absolute highest peaks to the left and right?
2.Can we maintain running running-maximums as pointers converge, eliminating redundant lookaheads?
3.Does the invariant guarantee that moving the pointer on the strictly smaller side is always safe because the opposing side acts as a safe upper bound?
By mapping these questions back to our elevation map trace, you can safely port the two-pointer sweep to higher-dimensional or modified architectural layouts without rewriting your core logic.
python
def max_container_area(height: list[int]) -> int:
    # Apply the two-pointer boundary-sweep pattern
    # to find the maximum rectangular container area.
    left, right = 0, len(height) - 1
    max_area = 0
    while left < right:
        # TODO: compute area and advance the limiting pointer
        pass
    return max_area

FAQ

How much water is trapped in the example [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]?
This elevation map traps a total of 6 units of water by accumulating liquid at index valleys bounded by taller left and right bars.
Why use two pointers instead of precomputing left and right maximum arrays?
The two-pointer approach optimizes space complexity from O(n) to O(1) by dynamically evaluating the limiting boundaries from both ends inward.
What is the time and space complexity of the optimal trapping rain water solution?
The two-pointer sweep runs in O(n) time complexity and O(1) auxiliary space complexity.
What is the primary pitfall when implementing the two-pointer approach?
A common mistake is moving the pointer with the larger maximum height instead of the smaller one, which compromises the accuracy of the bounding wall.

Keep learning