Editorial
Core insight
Container With Most Water · Two Pointers
Core Insight for Container With Most Water
The key intuition relies on the formula for the area:
To maximize the area, we want both the width and the height to be as large as possible. We start with the maximum possible width by placing pointers at the first and last indices.
From this state, we must decide which pointer to move inward. The decision is driven by the "bottleneck" of the container. The water level is constrained by the shorter line.
- If we keep the shorter line and move the taller line inward, the width decreases. The new height cannot exceed the existing shorter line (the bottleneck). Therefore, the area is guaranteed to decrease or stay the same.
- If we move the shorter line inward, the width decreases, but there is a possibility that we find a much taller line that compensates for the loss in width, resulting in a larger area.
Invariant: At every step, we can safely discard the shorter line because it cannot possibly form a larger area with any other line strictly inside the current window than it does with the current outer boundary.
Visual Description: Imagine the algorithm as a shrinking window. Initially, the window covers the entire array. We compare the values at the edges. If the left edge is lower than the right edge, we visually "cross out" the left index—it has reached its maximum potential area with the furthest possible pair (the right edge). We then shift the left boundary to the right. Conversely, if the right edge is lower, we cross it out and shift the right boundary to the left. We repeat this until the window closes.
Deep dive
Full breakdown
Full reasoning, pitfalls, and implementation flow
LeetCode 11: Container With Most Water Solution & Explanation
Problem Overview
TL;DR: The optimal solution utilizes a two-pointer approach starting from both ends of the array, iteratively moving the pointer pointing to the shorter line inward to maximize the potential area.
The "Container With Most Water" problem asks us to find two vertical lines within an array of line heights that, together with the x-axis, form a container capable of holding the maximum amount of water. The amount of water is determined by the area of the rectangle formed by the two lines. The height of this rectangle is limited by the shorter of the two lines, and the width is the distance between the indices of the lines.
This is a classic optimization problem found in LeetCode 11, often used in technical interviews to test understanding of greedy strategies and array manipulation.
Brute Force Approach for Container With Most Water
The most intuitive way to solve this problem is to calculate the area for every possible pair of lines and keep track of the maximum area found.
- Initialize a variable
maxAreato 0. - Use a nested loop structure. The outer loop iterates
ifrom0ton-1. - The inner loop iterates
jfromi+1ton-1. - For each pair
(i, j), calculate the width as(j - i). - Calculate the height as
min(height[i], height[j]). - Update
maxAreaif the current area is larger.
1# Pseudo-code for Brute Force
2max_area = 0
3for i from 0 to n-1:
4 for j from i+1 to n-1:
5 current_area = min(height[i], height[j]) * (j - i)
6 max_area = max(max_area, current_area)
7return max_areaTime Complexity: . We check pairs. Why it fails: The constraints state that can be up to . An solution results in approximately operations, which far exceeds the standard time limit (usually around operations per second). This leads to a Time Limit Exceeded (TLE) error on large test cases.
Algorithm Strategy: Two Pointer Patterns
We utilize the Two Pointer - Converging strategy to reduce the time complexity from quadratic to linear.
- Initialization: Create two pointers,
leftpointing to the beginning of the array (index 0) andrightpointing to the end (indexn-1). Initialize a variable to track the maximum area found so far. - Constraint Logic: The constraint is the relative height of the lines. The pointer pointing to the smaller height is always the limiting factor.
- Movement Rule:
- Calculate the current area defined by
leftandright. - Update the maximum area.
- If
height[left] < height[right], increment theleftpointer. This eliminates indexleftfrom future consideration. - Otherwise, decrement the
rightpointer. This eliminates indexrightfrom future consideration.
- Calculate the current area defined by
- Termination: The process continues until the
leftandrightpointers meet.
Execution Flow
Let's trace the algorithm with the input height = [1, 8, 6, 2, 5, 4, 8, 3, 7].
-
Start:
left = 0(val 1),right = 8(val 7).- Width = 8. Height = min(1, 7) = 1. Area = 8.
height[left] < height[right]. Moveleftto 1.
-
Step 2:
left = 1(val 8),right = 8(val 7).- Width = 7. Height = min(8, 7) = 7. Area = 49. (Max updated).
height[right] < height[left]. Moverightto 7.
-
Step 3:
left = 1(val 8),right = 7(val 3).- Width = 6. Height = min(8, 3) = 3. Area = 18.
height[right] < height[left]. Moverightto 6.
-
Step 4:
left = 1(val 8),right = 6(val 8).- Width = 5. Height = min(8, 8) = 8. Area = 40.
- Heights equal (arbitrarily move right). Move
rightto 5.
-
Step 5:
left = 1(val 8),right = 5(val 4).- Width = 4. Height = min(8, 4) = 4. Area = 16.
height[right] < height[left]. Moverightto 4.
-
Step 6:
left = 1(val 8),right = 4(val 5).- Width = 3. Height = min(8, 5) = 5. Area = 15.
height[right] < height[left]. Moverightto 3.
-
Step 7:
left = 1(val 8),right = 3(val 2).- Width = 2. Height = min(8, 2) = 2. Area = 4.
height[right] < height[left]. Moverightto 2.
-
Step 8:
left = 1(val 8),right = 2(val 6).- Width = 1. Height = min(8, 6) = 6. Area = 6.
height[right] < height[left]. Moverightto 1.
-
End:
leftmeetsright. Return Max Area: 49.
Proof of Correctness
To prove this greedy strategy works, consider the state where we have pointers at left and right, and assume height[left] < height[right].
The algorithm decides to move left to left + 1. This effectively discards all pairs (left, k) where left < k < right. Is it possible that one of these discarded pairs has a larger area than the current (left, right)?
- The width of any pair
(left, k)isk - left. Sincek < right, the widthk - leftis strictly less thanright - left. - The height of the pair
(left, k)ismin(height[left], height[k]). This value can be at mostheight[left]. - Therefore, the area of
(left, k)is at mostheight[left] * (k - left), which is strictly less thanheight[left] * (right - left).
Thus, the pair (left, right) provides the maximum possible area involving the index left. We can safely discard left and narrow our search space without missing the global maximum.
Pattern Reuse Notes
The Two Pointers - Converging pattern is a fundamental technique for optimizing array problems, particularly when searching for pairs or triplets that satisfy a condition. This pattern is directly applicable to:
- LeetCode 15: 3Sum - Uses a fixed pointer and two converging pointers to find triplets summing to zero.
- LeetCode 16: 3Sum Closest - Similar to 3Sum, but tracks the minimum difference to a target.
- LeetCode 18: 4Sum - Extends the pattern to four numbers, reducing complexity from to .
- LeetCode 167: Two Sum II - Input Array Is Sorted - The classic base case for this pattern where pointers move based on the sum relative to a target.
Feynman coach · Before you peek at the code
Can you explain the core insight in your own words?
If you can explain it before reading code, you’re far more likely to recall it under interview pressure.
Reference Implementation
C++ Solution for LeetCode 11
1class Solution {
2public:
3 int maxArea(vector<int>& height) {
4 int left = 0;
5 int right = height.size() - 1;
6 int max_area = 0;
7
8 while (left < right) {
9 // Calculate current area
10 int width = right - left;
11 int current_height = min(height[left], height[right]);
12 max_area = max(max_area, width * current_height);
13
14 // Move the pointer corresponding to the shorter line
15 if (height[left] < height[right]) {
16 left++;
17 } else {
18 right--;
19 }
20 }
21 return max_area;
22 }
23};Java Solution for LeetCode 11
1class Solution {
2 public int maxArea(int[] height) {
3 int left = 0;
4 int right = height.length - 1;
5 int maxArea = 0;
6
7 while (left < right) {
8 // Calculate current area
9 int width = right - left;
10 int currentHeight = Math.min(height[left], height[right]);
11 maxArea = Math.max(maxArea, width * currentHeight);
12
13 // Move the pointer corresponding to the shorter line
14 if (height[left] < height[right]) {
15 left++;
16 } else {
17 right--;
18 }
19 }
20 return maxArea;
21 }
22}Python Solution for LeetCode 11
1class Solution:
2 def maxArea(self, height: List[int]) -> int:
3 left = 0
4 right = len(height) - 1
5 max_area = 0
6
7 while left < right:
8 # Calculate current area
9 width = right - left
10 current_height = min(height[left], height[right])
11 max_area = max(max_area, width * current_height)
12
13 # Move the pointer corresponding to the shorter line
14 if height[left] < height[right]:
15 left += 1
16 else:
17 right -= 1
18
19 return max_area