Problem Walkthrough

Container With Most Water LeetCode 11

Two pointers start at the widest span and move inward greedily — here is the exchange-argument proof that this never misses the optimal pair.

8 min read|

Container With Most Water

LeetCode 11

Problem Overview

LeetCode 11 gives you an integer array height of length n. Each value height[i] represents a vertical line at x-coordinate i. You must choose two lines that, together with the x-axis, form a container holding the most water. The area of the container formed by lines i and j (where i < j) is: area = min(height[i], height[j]) * (j - i). Water spills over the shorter line, so the minimum height is the limiting factor.

The problem looks deceptively simple — just two nested loops, right? For small inputs yes, but n can be up to 100,000, so the brute-force O(n^2) approach will time out. The challenge is finding the optimal pair in a single linear pass.

Key constraints to keep in mind going into your solution design:

  • n is between 2 and 100,000 — brute force O(n^2) will time out
  • height[i] is between 0 and 10,000 — no negative heights
  • You must return the maximum area, not the indices themselves
  • The container bottom is always the x-axis — no tilted containers

Brute Force — Check All O(n^2) Pairs

The straightforward solution is two nested loops: for every pair (i, j) where i < j, compute area = min(height[i], height[j]) * (j - i) and track the running maximum. This is O(n^2) time and O(1) space.

For n = 100,000 this means up to 5 billion pair evaluations — roughly 5 to 10 seconds at typical processor speeds, well beyond any judge's time limit. Interviewers want you to name the brute force, explain its flaw, and then derive the linear solution.

The brute force is a valid starting point in a conversation. State it, prove it correct by exhaustion, then ask: "Is there a way to prune pairs we know cannot be optimal?" That question leads directly to the two-pointer insight.

💡

Key Insight

The key insight is that moving the shorter pointer inward is the only move that can possibly increase the area. Moving the taller pointer inward guarantees the area stays the same or shrinks — the width decreases while the height bottleneck is unchanged.

Two Pointer Strategy

Initialize two pointers: left = 0 (leftmost line) and right = n - 1 (rightmost line). This starting position gives the maximum possible width. Compute the area for this pair and record it as the running maximum.

At each step, move the pointer pointing to the shorter line one step inward. If both lines are equal height, move either pointer — it does not matter which (both are safe). Repeat until left and right meet.

Every iteration evaluates exactly one pair and advances one pointer, so the total iterations are at most n - 1. Time complexity O(n), space complexity O(1). The entire core logic fits in five lines.

  1. 1Set left = 0, right = n - 1, maxArea = 0
  2. 2While left < right: compute area = min(height[left], height[right]) * (right - left)
  3. 3Update maxArea = max(maxArea, area)
  4. 4If height[left] < height[right]: increment left, else decrement right
  5. 5Return maxArea when the pointers meet

Proof of Correctness

Why is it safe to discard the current shorter pointer and all pairings it has with indices between left and right? Suppose height[left] <= height[right]. Every pair (left, k) where left < k < right has width (k - left) < (right - left) and is still bounded by height[left] on the left. So area(left, k) <= height[left] * (k - left) < height[left] * (right - left) <= area(left, right). None of these inner pairs can beat the area we just recorded for (left, right).

Therefore we can safely advance left inward — every pair involving the old left value with any right-side index smaller than current right is already dominated. We have not skipped the optimal pair; we have proven it cannot use the current left. By symmetry, the same argument applies when height[right] is the shorter side.

This continues until left and right converge, having evaluated exactly the set of pairs that could have been optimal. No brute-force enumeration required.

ℹ️

Exchange Argument

This is an exchange argument: we prove every discarded pair is dominated by one we have already seen or will see. The two-pointer sweep is not a heuristic — it is a complete proof-backed elimination of suboptimal candidates.

Why Move the Shorter Side

Students often ask: why not move the taller pointer? Here is the direct argument. If we move the taller pointer inward, the new width is strictly smaller. The height of the new container is min(height[new_left_or_right], height[other]) which is at most the same height as before (bounded by the shorter side that did not move). So the area can only stay the same or decrease — we gain nothing.

Moving the shorter pointer is the only move that gives the area a chance to grow. If the next inward line is taller than the current shorter line, the new height bottleneck rises and might outweigh the width loss. Moving the taller side forecloses that possibility entirely.

Stated differently: the current area is bottlenecked by the shorter line. Eliminating that bottleneck is the only productive action at each step.

  1. 1Moving taller pointer: width decreases, height stays bounded by the shorter side — area cannot increase
  2. 2Moving shorter pointer: width decreases, but height bottleneck may rise — area can increase
  3. 3Equal heights: moving either pointer is safe; current area is already recorded
  4. 4Both pointers advance toward each other exactly once each — O(n) total moves

Code Walkthrough — Python and Java

Python: def maxArea(height): left, right, best = 0, len(height) - 1, 0 — then while left < right: best = max(best, min(height[left], height[right]) * (right - left)), and if height[left] < height[right]: left += 1, else: right -= 1 — return best. Five lines, O(n) time, O(1) space.

Java: public int maxArea(int[] height) { int left = 0, right = height.length - 1, best = 0; while (left < right) { best = Math.max(best, Math.min(height[left], height[right]) * (right - left)); if (height[left] < height[right]) left++; else right--; } return best; }. Identical logic, different syntax.

Both solutions handle all edge cases uniformly: n = 2 (single iteration), all equal heights, strictly increasing or decreasing arrays. The algorithm does not need special cases because the greedy proof covers all configurations.

⚠️

Equal Heights

When heights are equal, move either pointer. Both are correct because the current area is already recorded in maxArea. Many implementations always move left in this case — that is fine.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now