Given an elevation map, compute how much rainwater is trapped between the bars after it rains. The key insight: water above any bar equals the minimum of the tallest bars to its left and right minus its own height. Two inward-moving pointers track running maxima so you can compute this in O(n) time and O(1) space.
You receive an array height of non-negative integers. Each element is the height of a vertical bar of width 1. After rain, how many units of water are trapped between the bars?
Worked example: height = [0,1,0,2,1,0,1,3,2,1,2,1] → the answer is 6. The tall bar at index 7 (height 3) acts as the right wall for several shallow pits on its left; the bars at indices 2, 4, and 5 each trap 1–2 units.
i is determined by the shorter of the two tallest walls on either side: water[i] = min(leftMax[i], rightMax[i]) − height[i]. You don't need to know both walls simultaneously — two inward-moving pointers let you process whichever side has the shorter known wall, because that side's water level is already fully determined by its own running max.When height[l] <= height[r], we know leftMax <= rightMax (since height[r] is at least as tall), so the water at l is exactly leftMax − height[l] — the right wall is guaranteed to be taller. We can safely add water and advance l.
l=0, r=n−1, leftMax=rightMax=water=0. Two pointers squeeze inward.height[l] vs height[r]. The shorter side is the bottleneck — its water level is capped by its own running max, not the far side.runningMax − height[ptr] to water. (If the bar is taller than the max, the difference is 0 — no water, just a new max.)l == r. Return water.leftMax and rightMax arrays (O(n) space) before you can start. The two-pointer version computes the water on the fly by relying on the invariant that whichever pointer points to the shorter current height, the opposite side is guaranteed to be at least as tall — so the running max on the shorter side is the true limiting wall.Both approaches run in O(n) time. The stack-based approach (monotonic stack, LeetCode's official editorial) is also O(n) / O(n) and computes water horizontallyby layer rather than column-by-column; it's correct but harder to explain in an interview.
l=0, r=11. We'll advance whichever side has the shorter boundary.1function trap(height: number[]): number {2▶ let l = 0, r = height.length - 1;3▶ let leftMax = 0, rightMax = 0;4▶ let water = 0;56 while (l < r) {7 if (height[l] <= height[r]) {8 leftMax = Math.max(leftMax, height[l]);9 water += leftMax - height[l]; // left side guaranteed safe10 l++;11 } else {12 rightMax = Math.max(rightMax, height[r]);13 water += rightMax - height[r]; // right side guaranteed safe14 r--;15 }16 }17 return water;18}
function trap(height: number[]): number {
let l = 0, r = height.length - 1;
let leftMax = 0, rightMax = 0;
let water = 0;
while (l < r) {
if (height[l] <= height[r]) {
leftMax = Math.max(leftMax, height[l]);
water += leftMax - height[l]; // left side guaranteed safe
l++;
} else {
rightMax = Math.max(rightMax, height[r]);
water += rightMax - height[r]; // right side guaranteed safe
r--;
}
}
return water;
}l starts at the left edge, r at the right. leftMax and rightMax track the tallest bar seen so far from each side as the pointers squeeze inward. water accumulates the answer.height[l] <= height[r], the left boundary is the bottleneck. We know the right wall is at least as tall as height[r], and since height[r] >= height[l], the right wall is at least leftMax. So the water at l is exactly leftMax − height[l].leftMax — if height[l] is a new peak, the difference is 0 (no water, just a taller wall). Otherwise we add the trapped units and advance l.r with rightMax and step r inward.l == r the loop ends; every column has been accounted for. The single variable water holds the total — no auxiliary array needed.l or r. O(1) space — only four scalar variables beyond the input.waterAt[i] array during the same pass (or use the precomputed-max approach for clarity).min(l,r) * width; LC 42 accumulates min(leftMax,rightMax) − height[i] per column.function trap(height: number[]): number {
let l = 0, r = height.length - 1;
let leftMax = 0, rightMax = 0;
let water = 0;
while (l < r) {
if (height[l] <= height[r]) {
leftMax = Math.max(leftMax, height[l]);
water += leftMax - height[l]; // left side guaranteed safe
l++;
} else {
rightMax = Math.max(rightMax, height[r]);
water += rightMax - height[r]; // right side guaranteed safe
r--;
}
}
return water;
}l or r. O(1) space — only four scalar variables beyond the input.The clearest way to show your work: precompute both walls explicitly, then read off min(leftMax, rightMax) − height[i] per column.
function trap(height: number[]): number {
const n = height.length;
if (n === 0) return 0;
const leftMax: number[] = new Array(n);
const rightMax: number[] = new Array(n);
leftMax[0] = height[0];
for (let i = 1; i < n; i++) {
leftMax[i] = Math.max(leftMax[i - 1], height[i]);
}
rightMax[n - 1] = height[n - 1];
for (let i = n - 2; i >= 0; i--) {
rightMax[i] = Math.max(rightMax[i + 1], height[i]);
}
let water = 0;
for (let i = 0; i < n; i++) {
water += Math.min(leftMax[i], rightMax[i]) - height[i];
}
return water;
}Fills water in horizontal layers: each time a taller bar appears, pop the dip it spans and add a flat slab of water on top.
function trap(height: number[]): number {
const stack: number[] = []; // indices of bars, heights decreasing
let water = 0;
for (let i = 0; i < height.length; i++) {
// While the current bar is taller than the bar at the stack top,
// it forms the right wall of a trapped horizontal layer.
while (stack.length > 0 && height[i] > height[stack[stack.length - 1]]) {
const bottom = stack.pop()!; // floor of the basin
if (stack.length === 0) break; // no left wall, water spills out
const left = stack[stack.length - 1];
const width = i - left - 1;
const boundedHeight = Math.min(height[i], height[left]) - height[bottom];
water += width * boundedHeight;
}
stack.push(i);
}
return water;
}The literal definition: for every column, walk left and right to find its two walls. Good as a correctness baseline before you optimise.
function trap(height: number[]): number {
const n = height.length;
let water = 0;
for (let i = 0; i < n; i++) {
let leftMax = 0;
for (let j = i; j >= 0; j--) {
leftMax = Math.max(leftMax, height[j]);
}
let rightMax = 0;
for (let j = i; j < n; j++) {
rightMax = Math.max(rightMax, height[j]);
}
water += Math.min(leftMax, rightMax) - height[i];
}
return water;
}i itself, so min(leftMax, rightMax) >= height[i]and the per-column contribution is never negative. Quadratic time makes this impractical for large inputs — state it, then improve it.| "trapped water" or "elevation map" | two pointers with leftMax/rightMax |
| process the side with the shorter current bar | advance the shorter pointer |
| need min of two running maxima without precomputing both arrays | inward two-pointer squeeze |
| "Container With Most Water" variant | same inward-squeeze frame |
let l = 0, r = height.length - 1;
let leftMax = 0, rightMax = 0, water = 0;
while (l < r) {
if (height[l] <= height[r]) {
leftMax = Math.max(leftMax, height[l]);
water += leftMax - height[l];
l++;
} else {
rightMax = Math.max(rightMax, height[r]);
water += rightMax - height[r];
r--;
}
}
return water;min(leftMax[i], rightMax[i]) − height[i] — capped by the shorter wall.height = [0,1,0,2,1,0,1,3,2,1,2,1], what is the total trapped water?leftMax − height[l] equal when height[l] is a new peak?height[l] == height[r], which pointer should move?