Skip to main content

Container With Most Water

How to justify why only the shorter side makes sense to move and use that to shrink the search.

Given an array of integers height, where each value represents the height of a vertical line, find two lines that together with the x-axis form a container that stores the most water.

Return the maximum area.

Example:

Input:  height = [1,8,6,2,5,4,8,3,7]
Output: 49

What to notice before coding

Common mistakes

Step 1 — Understand the area formula

The area between two lines is:

width * usableHeight

Where:

  • width = right - left
  • usableHeight = min(height[left], height[right])

The shorter side is in charge.

Step 2 — Write the correct baseline

The baseline compares every possible pair:

export function maxArea(height: number[]): number {
  let best = 0

  for (let left = 0; left < height.length; left += 1) {
    for (let right = left + 1; right < height.length; right += 1) {
      const width = right - left
      const currentHeight = Math.min(height[left], height[right])
      best = Math.max(best, width * currentHeight)
    }
  }

  return best
}

It works. But it does not use any structure of the problem.

Step 3 — Ask which side is worth moving

If the left side is shorter, then it is limiting the area.

If you move the right side, the width shrinks and the limiting side stays the same.

That cannot improve the best area for that base pair.

So when one side is limiting, only that side is worth replacing.

Step 4 — Use two pointers with justified elimination

Start with the maximum possible width:

  • left at the beginning
  • right at the end

At each step:

  • compute the area
  • move the pointer at the smaller height
  • try to find a better limiting height even with smaller width

The interactive editor needs JavaScript. You can still read the prompt and copy the starter code below.

Starter code

export function maxArea(height: number[]): number {
  // your solution here
  return 0
}
solution.ts
Stuck? Hints available.

Haven't solved it yet?

Viewing the solution now may reduce learning. It's worth trying a bit more.

Open the reference solution

Without JavaScript, the reference solution is shown inline instead of in a dialog.

Solution

Final complexity

Time: O(n)

Space: O(1)

Solution 1 — Test every pair

Useful as a baseline, but too expensive.

export function maxArea(height: number[]): number {
  let best = 0

  for (let left = 0; left < height.length; left += 1) {
    for (let right = left + 1; right < height.length; right += 1) {
      const width = right - left
      const currentHeight = Math.min(height[left], height[right])
      best = Math.max(best, width * currentHeight)
    }
  }

  return best
}

Solution 2 — Two pointers with greedy reasoning

Now the search only moves where a real improvement is still possible.

export function maxArea(height: number[]): number {
  let left = 0
  let right = height.length - 1
  let best = 0

  while (left < right) {
    const width = right - left
    const currentHeight = Math.min(height[left], height[right])
    best = Math.max(best, width * currentHeight)

    if (height[left] <= height[right]) {
      left += 1
    } else {
      right -= 1
    }
  }

  return best
}

The value here is not only getting the answer. It is being able to justify the move.

What to say in the interview

A strong short explanation would be:

The area depends on width and the shorter side. I start with the widest possible container and, at each step, move the pointer at the shorter side. The reason is that moving the taller side does not fix the current bottleneck and still reduces width. So only the limiting side can open a chance for improvement.

That shows that you:

  • understood what really limits the area
  • justified the greedy choice
  • turned the algorithm into an argument, not a template

When this pattern shows up again

The same pattern comes back when you need to:

  • compare extremes
  • move only the side limiting the current answer
  • eliminate search with an impossibility argument
  • explain why a greedy choice makes sense

The point is not memorizing “move the smaller one.”

The point is justifying why moving the larger one does not help.

Next challenge Group Anagrams Previous challenge 3Sum

Related challenges

Related articles