Trapping Rain Water, is one of the most famous Hard coding problems in the coding interview prep world. It is a favorite for top companies like Amazon, Google, and Meta because it tests how well you can turn a complex idea into a fast, efficient solution. Mastering this problem is a huge milestone for any developer because it teaches you exactly how to optimize your code from a slow approach to a high-performance one.

Problem statement

You’re given an array, height, of non-negative integers, where each element represents the height of a vertical bar (width = 1). After raining, water can be trapped between the bars. Return the total amount of water that can be trapped.

Constraints:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

Examples

1 / 6

Naive approach: Checking every bar individually

A simple way to solve Trapping Rain Water is to compute the trapped water at every index separately. For each position, you look for the tallest wall on its left and the tallest wall on its right. The water level above that position is limited by the shorter of those two walls. If that level is higher than the current bar, the difference is the water trapped at that position.

This approach is easy to understand, but it is inefficient because it repeatedly scans the array to find left and right maxima for every index.

Key idea and intuition

The crucial insight is that if you already know one side has a smaller boundary than the other, then that smaller boundary fully determines the water level on that side. The taller side cannot reduce the water level below the smaller boundary, so you can safely compute trapped water for positions on the smaller-boundary side without needing to know the exact shape of the interior on the other side.

Rainwater can only be trapped when there is a boundary on both sides of a position. The water height at any index is determined by the smaller of the best boundary on the left and the best boundary on the right. In other words, a position cannot hold water higher than the shorter wall that encloses it.

An optimized solution using Two Pointers

The optimized solution uses two pointers, one starting at the left end and the other at the right end, and moves them toward the center. While scanning, it tracks the highest bar seen so far from the left and from the right.

At each step, it moves the pointer on the side with the smaller current maximum boundary, because that side is the limiting wall for any water trapped there. If the current bar is lower than that side’s maximum boundary, the difference between the boundary height and the bar height is the water trapped at that position, and that amount is added to the running total. If the current bar is higher, the maximum boundary is updated.

The process continues until the two pointers meet. Each position is handled at most once, and no extra arrays are needed, so the solution runs in linear time and constant space.

Step-by-step algorithm

  1. Set two pointers: left at the start of the array (0), and right at the end of the array (n - 1).
  2. Initialize two running boundaries: left_max as the height at left, and right_max as the height at right
  3. Initialize total_water = 0 to store the final answer.
  4. While left < right, repeat the following:
    1. Compare left_max and right_max.
      1. If left_max < right_max:
        1. Move left one step to the right.
        2. Update left_max to be the maximum of the current left_max and height[left].
        3. The water trapped at this position is left_max - height[left]. Add it to total_water.
    2. Otherwise (i.e., right_max <= left_max):
      1. Move right one step to the left.
      2. Update right_max to be the maximum of the current right_max and height[right].
      3. The water trapped at this position is right_max - height[right]. Add it to total_water.
  5. When the pointers meet, return total_water.

Let’s look at the slides below to better understand the solution algorithm.

1 / 5

Python code for the optimized solution

Below is the complete Python implementation for the optimized solution, including the two-pointer logic we discussed above.

Time complexity

Each element in the input array is visited exactly once. Regardless of how the heights are distributed, the pointers will meet in the middle after exactly n steps, where n is the number of bars in the elevation map or length of height. Therefore, the overall time complexity is O(n).

Space complexity

The space complexity is O(1) as the algorithm only uses a constant number of variables and no extra data structures.

Common mistakes to avoid

Before you finalize your implementation for Trapping Rain Water, make sure to avoid these common errors:

  • The “Global Max” Fallacy: Assuming water level is determined by the absolute tallest peak; in reality, it is limited by the shorter of the two tallest walls flanking a gap.
  • Missing the Subtraction: Forgetting to subtract the bar’s own height from the water level (Total = Level - height[i]), which results in calculating the total volume rather than just the trapped water.
  • Neglecting Edge Cases: Failing to handle arrays with fewer than 3 elements (length 0, 1, or 2), which are physically incapable of forming a valley to trap water.
  • Incorrect Pointer Sequencing: Moving the left or right pointer before calculating the water for that specific index or before updating the running maximum.
  • Index vs. Height Confusion: Accidentally using the pointer variable (the index) in mathematical operations instead of the actual value stored at that index (height[left]).