Container With Most Water

A row of walls, two pointers starting at the widest container, and a proof that moving the taller wall can never beat what you already have — so only the shorter side is ever worth moving.

7 min read
#algorithms
In one line

Start with the widest possible container, and always give up on the shorter wall — it can never be part of a better answer.

time O(n)space O(1)

Given an array of heights, height[i] is a vertical wall standing at index i. Any two walls i and j form a container that holds (j - i) * min(height[i], height[j]) units of water — width times the shorter wall, since water spills over the lower side. Find the pair that holds the most.

Checking every pair is O(n^2). The two-pointer trick gets it down to a single pass by starting at the widest container possible — the two ends — and proving, at each step, that one side can be discarded forever.

Why moving the taller wall never helps

Move the shorter wall

  • The current area is capped by min(left, right).
  • Moving the taller wall inward only shrinks the width while the cap stays the same or gets worse — it can't beat what you already have.
  • Moving the shorter wall is the only move that has a chance of raising the cap.

What it does not prove

  • It does not say the shorter wall's next position is better — only that keeping it can never be better.
  • It does not generalize to "hold water above every bar" — that's Trapping Rain Water, a different problem with a different invariant.
Signals in the problem statement
  • "array of heights" or "vertical lines/walls"
  • "container" or "maximum water/area between two lines"
  • asks for the best pair, not every pair
  • width times the shorter side, not the sum of both

The pattern

  1. 1

    Place pointers at index 0 and n - 1 — the widest span.

  2. 2

    Compute the area from the current width and the shorter wall, and keep the best seen so far.

  3. 3Move the pointer at the shorter wall inward.
  4. 4Stop when the pointers meet.
@itsnas max-area.ts
codemax-area.ts
function maxArea(height: number[]): number {
  let left = 0
  let right = height.length - 1
  let best = 0
 
  while (left < right) {
    const width = right - left
    const shorter = Math.min(height[left], height[right])
    best = Math.max(best, width * shorter)
 
    if (height[left] < height[right]) left++ // shorter wall moves
    else right--
  }
 
  return best
}
fn max_area(height: Vec<i32>) -> i32 {
    let (mut left, mut right) = (0usize, height.len() - 1);
    let mut best = 0;
 
    while left < right {
        let width = (right - left) as i32;
        let shorter = height[left].min(height[right]);
        best = best.max(width * shorter);
 
        if height[left] < height[right] {
            left += 1;
        } else {
            right -= 1;
        }
    }
 
    best
}
int maxArea(std::vector<int>& height) {
  int left = 0, right = height.size() - 1;
  int best = 0;
 
  while (left < right) {
    int width = right - left;
    int shorter = std::min(height[left], height[right]);
    best = std::max(best, width * shorter);
 
    if (height[left] < height[right]) left++;
    else right--;
  }
 
  return best;
}
func maxArea(height []int) int {
	left, right := 0, len(height) - 1
	best := 0
 
	for left < right {
		width := right - left
		shorter := min(height[left], height[right])
		if area := width * shorter; area > best {
			best = area
		}
 
		if height[left] < height[right] {
			left++ // shorter wall moves
		} else {
			right--
		}
	}
 
	return best
}
def max_area(height: list[int]) -> int:
    left, right = 0, len(height) - 1
    best = 0
 
    while left < right:
        width = right - left
        shorter = min(height[left], height[right])
        best = max(best, width * shorter)
 
        if height[left] < height[right]:
            left += 1  # shorter wall moves
        else:
            right -= 1
 
    return best
main
Naseebullah

Complexity

Time

Each wall is visited at most once as the pointers close in from both ends.

Space

Two pointers and a running max — no auxiliary structures.

Common pitfalls

1

Moving the taller wall

This is the whole bug surface of the problem — moving the taller wall can only shrink the width without raising the cap, so it silently throws away the correct answer.

2

Using the sum instead of the shorter wall

Water spills over the lower side. The area is width * min(left, right), not width * (left + right).

3

Forgetting to record the area before moving

Compute and compare the area at the current pair first, then decide which pointer to move — moving first silently skips positions.

4

Confusing it with Trapping Rain Water

That problem sums water above every bar and needs the running max from both directions, not just the two outer walls — a plain two-pointer max isn't enough on its own.

Variants worth knowing

  1. 1

    Equal walls — when left and right are the same height, moving either pointer is safe; both are equally capped.

  2. 2

    Trapping Rain Water — same opposite-ends setup, but tracks the running max from each side to sum water above every bar, not just the best single pair.

  3. 3

    Two Sum II — the identical "shrink the side that can't improve" proof, applied to a sum instead of an area.

Practice, easiest first


What's next in the notebook?

Keep reading — more from where that came from.

Valid Triangle Number

9 min read
#algorithms
Sort the side lengths, fix the largest one at a time, and let the gap between two pointers count every valid pair against it in one step instead of testing them one by one.

3Sum

12 min read
#algorithms
Sort the array, fix one value as a moving target, and reuse the exact two-pointer proof from Two Sum II to sweep the rest — with a couple of extra duplicate-skipping rules layered on top.

Two Sum II

8 min read
#algorithms
A sorted array, two pointers closing in from both ends, and a proof that whichever side is off-target can be ruled out entirely rather than retried against a smaller search space.

Two Pointers

7 min read
#algorithms
Two indices walking through one ordered structure, discarding the side that cannot improve the answer at every step and replacing a nested loop with a single pass.

Copyright © 2025-present itsnas.me 
All Rights Reserved.