Trapping rain water

Water above one bar is min(tallest left, tallest right) − its own height. The two-pointer version exploits one fact: whichever side is shorter is the binding constraint, so that side's water can be settled immediately. One pass, O(1) space.

Overview

The problem

Given an elevation map as an array of bar heights, compute how much water is trapped after rain.

For [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] the answer is 6.

The whole problem reduces to one observation about a single position:

water[i] = min(max height to the left, max height to the right) − height[i]

Water at a position is held by the shorter of the two walls surrounding it, and the bar itself occupies part of that space. If the result is negative — the bar is taller than one of its bounding walls — the water is zero.

Getting that formula stated is 80% of the problem. Everything after it is about computing the two maxima efficiently.

Lists & arraysCoding problemHard

Step through it

What to watch

  • Only the shorter side is advanced, and only that side's water is settled.
  • The running maxima are two integers, not two arrays.
  • Each bar is visited exactly once.

Say this out loud

"Per bar it's min of the max to the left and the max to the right, minus its height. Two pointers from both ends: always move the shorter side, because that side's maximum is what limits it. O(n) time, O(1) space."

Trapping rain water

Given bar heights, how much water is trapped between them?

Run it

The two-pointer sweep against the precomputed-arrays version and a brute force, with the extra memory each one uses printed — all three agreeing on every test.

1Python
Output
2Python
Output
3Python
Output

The brute force, then the fix

Scanning left and right from every position is O(n²):

4Python
Output

The wasted work is obvious: the same maxima are recomputed at every index. Precompute them once.

5Python
Output

O(n) time, O(n) space. Three passes, and each is a one-line recurrence. This is a perfectly good answer, and it is the clearest one to reason about.

The two-pointer solution: O(1) space

The insight that removes the arrays: only the smaller of the two running maxima matters.

If left_max < right_max, then whatever lies between, the water level at the left pointer is decided by left_max — because a wall at least as tall as right_max exists somewhere to the right, and right_max is the larger of the two. So the left position can be finalised without knowing the exact right-hand maximum.

6Python
Output

O(n) time, O(1) space, one pass.

The comparison height[left] < height[right] is what guarantees the shorter side is being processed, and therefore that the running maximum on that side is the binding constraint. Because the maximum is updated before the addition, left_max - height[left] is never negative — which is why no max(0, ...) guard is needed.

The per-bar formula

Water sits above a bar up to the level of the lower of the two walls containing it: min(max to the left, max to the right) − height, or zero if that is negative.

The direct implementation precomputes both arrays of running maxima and then sums. That is O(n) time and O(n) space, perfectly correct, and the right first answer. The two-pointer version removes the arrays.

Why moving the shorter side is safe

Suppose height[lo] < height[hi]. Then whatever the maxima turn out to be, the left side's is the smaller of the two — because there is already a bar at least as tall as height[hi] on the right. So the water above lo is decided by left_max alone and can be settled now, without knowing anything more about the right.

That is the whole argument, and it is worth stating explicitly. Candidates who write this from memory usually cannot say why moving the shorter side is the correct choice.

The stack alternative

A monotonic decreasing stack also solves it in O(n), filling water horizontally layer by layer rather than column by column. It is harder to get right under pressure and worth naming as an alternative.

The same "maximum to the left and right of each element" shape appears in largest-rectangle-in-a-histogram and stock-span, so recognising it is worth more than memorising this one solution.

Working through the example

[0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1], computing per-position water with the prefix arrays:

i01234567891011
height010210132121
left max011222233333
right max333333332221
min011222232221
water001012100100

Total: 1 + 1 + 2 + 1 + 1 = 6.

Two rows are instructive. At i = 7 the bar is height 3 and is the tallest, so min(3, 3) - 3 = 0 — the peak holds nothing. At i = 5 the bar is 0 with walls of 2 and 3, so it holds 2 units, bounded by the shorter wall.

The stack solution

A monotonic decreasing stack also solves it, filling water in horizontal layers as each new bar pops shorter bars off:

7Python
Output

O(n) time, O(n) space. It computes the same total by region rather than by column, which is why the arithmetic looks so different.

Worth knowing because the monotonic-stack pattern solves an entire family — largest rectangle in a histogram, next greater element, daily temperatures. For this problem specifically, two pointers is the better answer: less code, less space, easier to explain.

ApproachTimeSpaceNotes
Brute forceO(n²)O(1)The baseline
Prefix maximaO(n)O(n)Clearest to explain
Two pointersO(n)O(1)The best answer
Monotonic stackO(n)O(n)Same pattern as histogram problems

Edge cases

CaseAnswer
[]0
[1], [1, 2]0 — needs at least three bars
Monotonically increasing0 — no right wall
Monotonically decreasing0 — no left wall
[3, 0, 3]3
All equal heights0
[0, 0, 0]0

The monotonic rows are the useful mental check: water needs a wall on both sides, so any sorted input traps nothing.

Container with most water. Also two pointers, and a different rule — there the area is min(h[l], h[r]) * (r - l) and the shorter side is moved because it is the limiting factor.

Largest rectangle in a histogram. Monotonic stack, and the closest relative of the stack solution here.

Trapping rain water II (2D grid). Substantially harder: a min-heap starting from the boundary, processing cells in increasing height order — essentially Dijkstra's algorithm.

Product of array except self. Unrelated in topic, identical in technique: prefix and suffix arrays, then combine.

What it is testing

Do you find the per-position formula? min(left_max, right_max) - height[i] is the insight; without it there is nothing to optimise.

Do you eliminate the repeated maxima? Precomputing them is the step from O(n²) to O(n).

Do you reach O(1) space? The two-pointer argument — only the smaller maximum binds — is the part that requires real thought.

Can you justify moving the shorter side? Being able to explain why that is safe distinguishes understanding from memorisation.

Do you check the degenerate inputs? Empty, sorted, and fewer than three bars.

Recap in one screen

  • Water above a bar is min(left_max, right_max) - height[i], never negative.
  • Precomputing the two maxima arrays gives O(n) time and O(n) space in three simple passes.
  • Two pointers reach O(1) space: process whichever side is shorter, since its running maximum is the binding wall.
  • A monotonic stack solves it by regions instead of columns — the same pattern as the histogram problems.
  • Any monotonic input traps nothing, because water needs walls on both sides.

How the code works

The two-pointer sweep against the precomputed-arrays version and a brute force, with the extra memory each one uses printed — all three agreeing on every test.

How the code works

  1. if heights[lo] < heights[hi]:The decision the whole approach rests on. The shorter side is necessarily the smaller of the two maxima, so its water is already determined.
  2. total += left_max - heights[lo]Settled immediately, with no knowledge of the right side. That is only valid because of the comparison above — be ready to say why.
  3. left_max = max(left_max, heights[lo])Two integers replace the two arrays of the previous version. The algorithm is the same; only the bookkeeping shrank.
  4. while lo < hiStrictly less than. The two pointers meeting means every bar has been settled exactly once.

Change one thing

  • Feed it a strictly increasing list. The answer is zero — there is no right wall to hold anything.
  • Print lo, hi and both maxima each iteration and check that the shorter side is always the one that moves.

Where this runs

Real CPython, compiled to WebAssembly and running on your own machine — nothing is uploaded. The first run takes a few seconds while the interpreter downloads; after that it is immediate. Need more room, or want to paste your own attempt? Use the Python compiler.

Check yourself

0 of 3

Answer without scrolling back up.

  1. The water above a single bar equals:

  2. Why is it safe to settle the shorter side's water immediately?

  3. The two-pointer version improves on the precomputed-arrays version in:

Cheat sheet

Trapping rain water

Water above one bar is min(tallest left, tallest right) − its own height. The two-pointer version exploits one fact: whichever side is shorter is the binding constraint, so that side's water can be settled immediately. One pass, O(1) space.

INTERVIEW · vizlearn.in/interview/trapping-rain-water.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.