Signal
"Maximize area/volume between two chosen boundaries" over an array — you're
picking two positions and the value depends on both how far apart they are and
the smaller of two heights. That width-times-min-height shape is what makes
converging pointers from both ends the tool, even though nothing here is
sorted.
Approach
Start with the widest possible container: pointers at the very first and very
last line. Its area is width times the shorter line. Now, the only way to find
something better is to increase the shorter side — width can only shrink from
here, so keeping the same short side and shrinking width can never win. So move
whichever pointer points at the shorter line inward, and repeat, tracking the
best area seen.
Skeleton
left, right = 0, len(height) - 1
best = 0
while left < right:
best = max(best, (right - left) * min(height[left], height[right]))
if height[left] < height[right]: left += 1
else: right -= 1
Solution
def max_area(height: list[int]) -> int:
left, right = 0, len(height) - 1
best = 0
while left < right:
width = right - left
best = max(best, width * min(height[left], height[right]))
if height[left] < height[right]:
left += 1
else:
right -= 1
return best
Complexity
O(n) time — each pointer moves inward at most n times total. O(1) space.
Pitfalls
- The classic wrong move: shrinking the taller side. It feels symmetric but
is provably never better — width drops and height is still capped by the
short side, so the area can only stay the same or shrink.
- Trying every pair (O(n²)) because the "maximize over all pairs" phrasing
reads like it needs brute force — the two-pointer argument above is what
gets you to O(n).
- Off-by-one on width: it's
right - left, the gap between indices, not the
number of lines spanned.