11. Container With Most Water - Explanation

Problem Link

Description

You are given an integer array heights where heights[i] represents the height of the ithi^{th} bar.

You may choose any two bars to form a container. Return the maximum amount of water a container can store.

Example 1:

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

Output: 36

Example 2:

Input: height = [2,2,2]

Output: 4

Constraints:

  • 2 <= height.length <= 1000
  • 0 <= height[i] <= 1000


Recommended Time & Space Complexity

You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array.


Hint 1

A brute force solution would be to try all pairs of bars in the array, compute the water for each pair, and return the maximum water among all pairs. This would be an O(n^2) solution. Can you think of a better way?


Hint 2

Can you think of an algorithm that runs in linear time and is commonly used in problems that deal with pairs of numbers? Find a formula to calculate the amount of water when we fix two heights.


Hint 3

We can use the two pointer algorithm. One pointer is at the start and the other at the end. At each step, we calculate the amount of water using the formula (j - i) * min(heights[i], heights[j]). Then, we move the pointer that has the smaller height value. Can you think why we only move the pointer at smaller height?


Hint 4

In the formula, the amount of water depends only on the minimum height. Therefore, it is appropriate to replace the smaller height value.


Company Tags

Please upgrade to NeetCode Pro to view company tags.



1. Brute Force

Intuition

We try every possible pair of lines and compute the area they form.
For each pair (i, j), the height of the container is the shorter of the two lines, and the width is the distance between them.
By checking all pairs, we are guaranteed to find the maximum area.

Algorithm

  1. Initialize res = 0 to track the maximum area found.
  2. Use two nested loops:
    • Outer loop picks the left line i.
    • Inner loop picks the right line j > i.
  3. For each pair (i, j):
    • Compute the height as min(heights[i], heights[j]).
    • Compute the width as j - i.
    • Update res with the maximum of its current value and the new area.
  4. After checking all pairs, return res.
class Solution:
    def maxArea(self, heights: List[int]) -> int:
        res = 0
        for i in range(len(heights)):
            for j in range(i + 1, len(heights)):
                res = max(res, min(heights[i], heights[j]) * (j - i))
        return res

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(1)O(1)

2. Two Pointers

Intuition

Using two pointers lets us efficiently search for the maximum area without checking every pair.
We start with the widest container (left at start, right at end).
The height is limited by the shorter line, so to potentially increase the area, we must move the pointer at the shorter line inward.
Moving the taller line never helps because it keeps the height the same but reduces the width.
By always moving the shorter side, we explore all meaningful possibilities.

Algorithm

  1. Initialize two pointers:
    • l = 0
    • r = len(heights) - 1
  2. Set res = 0 to store the maximum area.
  3. While l < r:
    • Compute the current area:
      area = min(heights[l], heights[r]) * (r - l)
    • Update res with the maximum area so far.
    • Move the pointer at the shorter height:
      • If heights[l] <= heights[r], move l right.
      • Otherwise, move r left.
  4. Return res after the pointers meet.
class Solution:
    def maxArea(self, heights: List[int]) -> int:
        l, r = 0, len(heights) - 1
        res = 0

        while l < r:
            area = min(heights[l], heights[r]) * (r - l)
            res = max(res, area)
            if heights[l] <= heights[r]:
                l += 1
            else:
                r -= 1
        return res

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(1)O(1)

Common Pitfalls

Moving the Wrong Pointer

The algorithm requires moving the pointer at the shorter height inward. Moving the taller pointer instead never increases the area (since height is limited by the shorter side) and can cause the algorithm to miss the optimal solution.

Confusing This Problem with Trapping Rain Water

Unlike the Trapping Rain Water problem where you need to sum water trapped between bars, this problem finds a single container formed by two lines. Applying the wrong mental model leads to incorrect area calculations.

Off-by-One Errors in Width Calculation

The width between indices l and r is r - l, not r - l + 1. Using the wrong formula overestimates the area by one unit for every pair, leading to incorrect results.