42. Trapping Rain Water - Explanation

Problem Link

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 maximum area of water that can be trapped between the bars.

Example 1:

Input: height = [0,2,0,3,1,0,1,3,2,1]

Output: 9

Constraints:

  • 1 <= height.length <= 1000
  • 0 <= height[i] <= 1000


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].


Company Tags

Please upgrade to NeetCode Pro to view company tags.



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

  1. If the input list is empty, return 0.
  2. Let n be the length of the array and initialize res = 0.
  3. For each index i:
    • Compute leftMax by scanning from index 0 to i.
    • Compute rightMax by scanning from index i + 1 to the end.
    • Add min(leftMax, rightMax) - height[i] to res.
  4. After processing all positions, return res.
Example - Dry Run

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │  height
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Step-by-step execution:

For each position, we scan left and right to find the maximum heights.

i height[i] leftMax rightMax min(L,R) Water = min(L,R) - height[i]
0 0 0 3 0 0 - 0 = 0
1 1 1 3 1 1 - 1 = 0
2 0 1 3 1 1 - 0 = 1
3 2 2 3 2 2 - 2 = 0
4 1 2 3 2 2 - 1 = 1
5 0 2 3 2 2 - 0 = 2
6 1 2 3 2 2 - 1 = 1
7 3 3 3 3 3 - 3 = 0
8 2 3 2 2 2 - 2 = 0
9 1 3 2 2 2 - 1 = 1
10 2 3 2 2 2 - 2 = 0
11 1 3 1 1 1 - 1 = 0

Total water trapped = 1 + 1 + 2 + 1 + 1 = 6


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 res

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(1)O(1)

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 index i
  • rightMax[i] = tallest bar from the end up to index i

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

  1. If the array is empty, return 0.
  2. Create two arrays:
    • leftMax of size n
    • rightMax of size n
  3. Fill leftMax:
    • leftMax[0] = height[0]
    • For each i from 1 to n - 1,
      leftMax[i] = max(leftMax[i - 1], height[i])
  4. Fill rightMax:
    • rightMax[n - 1] = height[n - 1]
    • For each i from n - 2 down to 0,
      rightMax[i] = max(rightMax[i + 1], height[i])
  5. Compute trapped water:
    • For each index i, add min(leftMax[i], rightMax[i]) - height[i] to the result.
  6. Return the total trapped water.
Example - Dry Run

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │  height
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Step 1: Build leftMax array (scan left to right)

  leftMax[i] = max height from index 0 to i

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 1 │ 2 │ 2 │ 2 │ 2 │ 3 │ 3 │ 3 │ 3 │ 3 │  leftMax
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Step 2: Build rightMax array (scan right to left)

  rightMax[i] = max height from index i to n-1

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 3 │ 3 │ 3 │ 3 │ 3 │ 3 │ 3 │ 3 │ 2 │ 2 │ 2 │ 1 │  rightMax
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Step 3: Calculate water at each position

Water at index i = min(leftMax[i], rightMax[i]) - height[i]

i height[i] leftMax[i] rightMax[i] min(L,R) Water
0 0 0 3 0 0
1 1 1 3 1 0
2 0 1 3 1 1
3 2 2 3 2 0
4 1 2 3 2 1
5 0 2 3 2 2
6 1 2 3 2 1
7 3 3 3 3 0
8 2 3 2 2 0
9 1 3 2 2 1
10 2 3 2 2 0
11 1 3 1 1 0

Total water trapped = 1 + 1 + 2 + 1 + 1 = 6


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 res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

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

  1. Create an empty stack and set res = 0.
  2. 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.
  3. Return res after the loop finishes.
Example - Dry Run

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │  height
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Stack-based approach: Process horizontally layer by layer

The stack stores indices. When we find a taller bar, we pop and calculate water trapped.

Step-by-step trace:

Step i height[i] Stack (indices) Action Water
1 0 0 [] Push 0 0
2 1 1 [0] Pop 0, no left wall; Push 1 0
3 2 0 [1] Push 2 0
4 3 2 [1,2] Pop 2, calculate water; Push 3 1
5 4 1 [3] Push 4 0
6 5 0 [3,4] Push 5 0
7 6 1 [3,4,5] Pop 5, calculate water; Push 6 1
8 7 3 [3,4,6] Pop 6,4,3, calculate water; Push 7 3
9 8 2 [7] Push 8 0
10 9 1 [7,8] Push 9 0
11 10 2 [7,8,9] Pop 9, calculate water; Push 10 1
12 11 1 [7,8,10] Push 11 0

Total water trapped = 1 + 1 + 3 + 1 = 6


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 res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

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

  1. Set two pointers:
    • l at the start
    • r at the end
      Track leftMax and rightMax as the tallest walls seen.
  2. While l < r:
    • If leftMax < rightMax:
      • Move l right.
      • Update leftMax.
      • Add leftMax - height[l] to the result.
    • Else:
      • Move r left.
      • Update rightMax.
      • Add rightMax - height[r] to the result.
  3. Return the total trapped water.
Example - Dry Run

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │  height
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Two Pointers Approach

Step 1: Initial State

   L                                           R
   ↓                                           ↓
  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

  leftMax = 0    rightMax = 1    water = 0

Step 3: Water trapped at index 2

leftMax < rightMax, so process left side. Water = leftMax - height[2] = 1 - 0 = 1

           L                                   R
           ↓                                   ↓
  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11
            ≈

  leftMax = 1    rightMax = 2    water = 1

Step 5: Water trapped at index 9

leftMax >= rightMax, so process right side. Water = rightMax - height[9] = 2 - 1 = 1

               L                       R
               ↓                       ↓
  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11
            ≈                           ≈

  leftMax = 2    rightMax = 2    water = 2

Steps 8-10: Water trapped at indices 4, 5, 6

Processing left side as leftMax < rightMax

                           L       R
                           ↓       ↓
  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11
            ≈       ≈   ≈   ≈           ≈

  leftMax = 2    rightMax = 3    water = 6

Complete step-by-step trace:

Step l r height[l] height[r] leftMax rightMax Action Water Total
1 0 11 0 1 0 1 l++ 0 0
2 1 11 1 1 1 1 r-- 0 0
3 1 10 1 2 1 2 l++ 1 1
4 2 10 0 2 1 2 l++ 0 1
5 3 10 2 2 2 2 r-- 1 2
6 3 9 2 1 2 2 r-- 0 2
7 3 8 2 2 2 2 r-- 0 2
8 3 7 2 3 2 3 l++ 1 3
9 4 7 1 3 2 3 l++ 2 5
10 5 7 0 3 2 3 l++ 1 6
11 6 7 1 3 2 3 l++ 0 6

Loop ends when l = 7 = r

Final Result:

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 1 │ 0 │ 2 │ 1 │ 0 │ 1 │ 3 │ 2 │ 1 │ 2 │ 1 │  height
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  │ 0 │ 0 │ 1 │ 0 │ 1 │ 2 │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │  water
  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘
    0   1   2   3   4   5   6   7   8   9  10  11

Total water trapped = 0 + 0 + 1 + 0 + 1 + 2 + 1 + 0 + 0 + 1 + 0 + 0 = 6


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 res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(1)O(1)

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.