MediumTwo Pointers

Container With Most Water โ€” Solution

Problem

You're given an array of integers where each value represents the height of a vertical bar. Pick two bars to form the sides of a container โ€” the amount of water it holds equals the shorter bar's height multiplied by the horizontal distance between them. Find the pair of bars that maximizes the volume of water.

  • Input: height = [1,8,6,2,5,4,8,3,7]
  • Output: 49
  • Explanation: Bars at index 1 (height 8) and index 8 (height 7) form a container holding min(8,7) ร— 7 = 49 units of water.

Intuition

Water can only fill up to the shorter of the two walls โ€” a 100-meter wall is useless if the other side is 1 meter. The brute-force approach checks every pair, but there's a smarter observation: starting from the outermost bars gives the widest possible container. The only way to potentially find a larger container is to swap out the shorter bar for something taller, because keeping the shorter bar and narrowing the width can never increase the area. This "always move the shorter pointer" rule is greedy and provably safe โ€” it never skips over a better answer.

Approach 1 โ€” Brute Force

Try every possible pair of bars and track the maximum water volume seen.

  1. Iterate over every pair of indices (left, right) where left < right.
  2. Compute water as min(height[left], height[right]) * (right - left).
  3. Update the running maximum.
  4. Return the maximum after all pairs are checked.
1def maxArea(self, height: list[int]) -> int:
2    max_water = 0
3    n = len(height)
4
5    for left in range(n):
6        for right in range(left + 1, n):
7            # Water is capped by the shorter wall, spread across the gap
8            current_water = min(height[left], height[right]) * (right - left)
9            max_water = max(max_water, current_water)
10
11    return max_water

Time: O(nยฒ) โ€” we examine every pair of bars.
Space: O(1) โ€” no extra storage needed.

Approach 2 โ€” Two Pointers

Start at the widest possible container and converge inward, always discarding the shorter bar.

  1. Initialize left = 0 and right = len(height) - 1.
  2. Compute the water for the current pair and update the maximum.
  3. Moving the taller bar inward shrinks the width while the effective height stays capped at the same shorter bar โ€” area can only decrease. Moving the shorter bar inward at least gives a chance of finding a taller wall. So always advance the pointer at the shorter bar.
  4. Repeat until the two pointers meet.
1def maxArea(self, height: list[int]) -> int:
2    left, right = 0, len(height) - 1
3    max_water = 0
4
5    while left < right:
6        # Water is capped by the shorter wall, spread across the gap
7        current_water = min(height[left], height[right]) * (right - left)
8        max_water = max(max_water, current_water)
9
10        # Moving the taller wall inward can't improve the result; eliminate the shorter one
11        if height[left] < height[right]:
12            left += 1
13        else:
14            right -= 1
15
16    return max_water

Time: O(n) โ€” each pointer moves at most n steps total.
Space: O(1) โ€” only two pointer variables.

Complexity Summary

ApproachTimeSpaceWhen to use
Brute ForceO(nยฒ)O(1)Verifying correctness or when n is very small
Two PointersO(n)O(1)Always โ€” the standard solution for this problem

Common Mistakes

  • Moving the taller pointer instead of the shorter one โ€” if you move the taller pointer inward, you shrink the width while the height is still capped by the shorter bar. The area can only decrease. You must move the shorter bar's pointer to have any hope of improvement.
  • Computing area as height[left] * height[right] โ€” water can only fill to the height of the shorter wall, and the width is the index gap, not related to the heights themselves.
  • Using left <= right as the loop condition โ€” when left == right you're pointing at a single bar, which forms no container. Use left < right.
  • Second-guessing the equal-height case โ€” when height[left] == height[right], both bars are the bottleneck and moving either pointer is safe. The else branch (decrement right) handles this correctly without a special case.
  • Confusing this problem with Trapping Rain Water โ€” in that problem every bar traps water independently using left/right maximum arrays; here only the two chosen endpoint bars form the walls and the bars between them are irrelevant.

Related Problems

  • trapping-rain-water โ€” same water theme but every interior bar traps water, requiring separate left/right maximum tracking rather than a single converging pair
  • two-sum-ii-input-array-is-sorted โ€” the canonical two-pointer problem on a sorted array, same left/right convergence pattern
  • boats-to-save-people โ€” two-pointer greedy that also eliminates one end per step, pairing heaviest with lightest
  • 3sum โ€” extends the two-pointer idea to three values by fixing one element with an outer loop
  • squares-of-a-sorted-array โ€” two-pointer technique that processes from both ends inward and builds the result in reverse

Ready to practice? Try it on SkillFlow

Adaptive problems, AI follow-up interviews, and a skill score that shows exactly where you need to improve.

Practice This Problem โ†’