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: 9Constraints:
1 <= height.length <= 10000 <= height[i] <= 1000
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.
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.
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].
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.
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].
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.
0.n be the length of the array and initialize res = 0.i:leftMax by scanning from index 0 to i.rightMax by scanning from index i + 1 to the end.min(leftMax, rightMax) - height[i] to res.res.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 11Step-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 resInstead 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 irightMax[i] = tallest bar from the end up to index iOnce 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.
0.leftMax of size nrightMax of size nleftMax:leftMax[0] = height[0]i from 1 to n - 1,leftMax[i] = max(leftMax[i - 1], height[i])rightMax:rightMax[n - 1] = height[n - 1]i from n - 2 down to 0,rightMax[i] = max(rightMax[i + 1], height[i])i, add min(leftMax[i], rightMax[i]) - height[i] to the result.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 11Step 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 11Step 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 11Step 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 resThe 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.
res = 0.i:height[i] is taller than the bar at the stack's top:res.res after the loop finishes.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 11Stack-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 resWater 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
l at the startr at the endleftMax and rightMax as the tallest walls seen.l < r:leftMax < rightMax:l right.leftMax.leftMax - height[l] to the result.r left.rightMax.rightMax - height[r] to the result.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 11Two 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 = 0Step 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 = 1Step 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 = 2Steps 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 = 6Complete 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 11Total 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 resThe 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.
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]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])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.