Container With Most Water
Overview
Container With Most Water problem finds two lines that together with x-axis form a container holding the most water. Unlike trapping rain water, this problem focuses on maximizing the area between two lines.
The key insight: area = width Ă min(height[left], height[right]). Use two pointers, always move the pointer with smaller height, as moving the taller one can only decrease area.
Container with water is particularly useful for:
- Understanding two-pointer technique
- Greedy algorithm thinking
- Area optimization problems
- Real-world container problems
Pattern 1: Two Pointers (Optimal)
Most efficient O(n) solution.
function maxArea(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
const area = width * minHeight;
maxWater = Math.max(maxWater, area);
// Move pointer with smaller height
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxWater;
}Time: O(n), Space: O(1)
Key insight: Always move pointer with smaller height. Moving taller pointer can only decrease area.
Pattern 2: Brute Force
Check all pairs of lines.
function maxAreaBruteForce(height: number[]): number {
let maxWater = 0;
for (let i = 0; i < height.length; i++) {
for (let j = i + 1; j < height.length; j++) {
const width = j - i;
const minHeight = Math.min(height[i], height[j]);
const area = width * minHeight;
maxWater = Math.max(maxWater, area);
}
}
return maxWater;
}Time: O(n²), Space: O(1)
Not recommended for large inputs.
Pattern 3: Two Pointers with Early Termination
Optimized with early termination.
function maxAreaOptimized(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
maxWater = Math.max(maxWater, width * minHeight);
// Move pointer with smaller height
// Skip all heights smaller than current min
if (height[left] < height[right]) {
const currentLeft = height[left];
while (left < right && height[left] <= currentLeft) {
left++;
}
} else {
const currentRight = height[right];
while (left < right && height[right] <= currentRight) {
right--;
}
}
}
return maxWater;
}Time: O(n), Space: O(1)
Optimization: Skip heights that can't improve area.
Pattern 4: Container With Most Water (Return Indices)
Return indices of the two lines.
function maxAreaWithIndices(height: number[]): [number, number, number] {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
let bestLeft = 0;
let bestRight = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
const area = width * minHeight;
if (area > maxWater) {
maxWater = area;
bestLeft = left;
bestRight = right;
}
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return [maxWater, bestLeft, bestRight];
}Time: O(n), Space: O(1)
Pattern 5: Container With K Most Water
Find K containers with most water.
function kMaxContainers(height: number[], k: number): Array<[number, number, number]> {
const containers: Array<[number, number, number]> = [];
// Generate all valid containers
for (let i = 0; i < height.length; i++) {
for (let j = i + 1; j < height.length; j++) {
const width = j - i;
const minHeight = Math.min(height[i], height[j]);
const area = width * minHeight;
containers.push([area, i, j]);
}
}
// Sort by area descending
containers.sort((a, b) => b[0] - a[0]);
return containers.slice(0, k);
}Time: O(n² log n), Space: O(n²)
Pattern 6: Container With Most Water (3D)
Find container with most water in 3D grid.
function maxArea3D(grid: number[][]): number {
const rows = grid.length;
const cols = grid[0].length;
let maxArea = 0;
// Try all pairs of rows
for (let i = 0; i < rows; i++) {
for (let j = i + 1; j < rows; j++) {
// For each column, find min height between rows
let area = 0;
for (let k = 0; k < cols; k++) {
const minHeight = Math.min(grid[i][k], grid[j][k]);
area += minHeight;
}
maxArea = Math.max(maxArea, area * (j - i));
}
}
return maxArea;
}Time: O(rows² * cols), Space: O(1)
Pattern 7: Container With Most Water (With Obstacles)
Find container accounting for obstacles.
function maxAreaWithObstacles(height: number[], obstacles: Set<number>): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
// Skip obstacles at boundaries
while (left < right && obstacles.has(left)) left++;
while (left < right && obstacles.has(right)) right--;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
maxWater = Math.max(maxWater, width * minHeight);
if (height[left] < height[right]) {
left++;
while (left < right && obstacles.has(left)) left++;
} else {
right--;
while (left < right && obstacles.has(right)) right--;
}
}
return maxWater;
}Time: O(n), Space: O(n)
Pattern 8: Container With Most Water (Circular)
Find container in circular array.
function maxAreaCircular(height: number[]): number {
const n = height.length;
let maxWater = 0;
// Try all starting positions
for (let start = 0; start < n; start++) {
let left = start;
let right = (start + 1) % n;
while (right !== start) {
const width = (right - left + n) % n;
const minHeight = Math.min(height[left], height[right]);
maxWater = Math.max(maxWater, width * minHeight);
if (height[left] < height[right]) {
left = (left + 1) % n;
if (left === start) break;
} else {
right = (right - 1 + n) % n;
if (right === start) break;
}
}
}
return maxWater;
}Time: O(n²), Space: O(1)
Pattern 9: Container With Most Water (Multiple Containers)
Find maximum total water from multiple non-overlapping containers.
function maxMultipleContainers(height: number[], k: number): number {
const n = height.length;
// DP: dp[i][j] = max water using first i lines with j containers
const dp = Array(n + 1).fill(null).map(() => Array(k + 1).fill(0));
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= k; j++) {
dp[i][j] = dp[i - 1][j];
// Try all possible left boundaries
for (let left = 0; left < i; left++) {
const width = i - left - 1;
const minHeight = Math.min(height[left], height[i - 1]);
const area = width * minHeight;
dp[i][j] = Math.max(dp[i][j], dp[left][j - 1] + area);
}
}
}
return dp[n][k];
}Time: O(n² k), Space: O(n k)
Pattern 10: Container With Most Water (Greedy Variants)
Different greedy strategies.
// Strategy 1: Always move towards taller line
function maxAreaGreedy1(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
maxWater = Math.max(maxWater, width * minHeight);
// Move towards potentially taller line
if (height[left + 1] > height[right - 1]) {
left++;
} else {
right--;
}
}
return maxWater;
}
// Strategy 2: Move pointer with smaller next height
function maxAreaGreedy2(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
maxWater = Math.max(maxWater, width * minHeight);
// Move pointer with smaller next height
const nextLeft = left + 1 < height.length ? height[left + 1] : 0;
const nextRight = right - 1 >= 0 ? height[right - 1] : 0;
if (nextLeft > nextRight) {
left++;
} else {
right--;
}
}
return maxWater;
}Note: These variants may not always find optimal solution.
When to Use Container With Water Techniques
Use container with water techniques when:
- Problem asks for maximum area between two lines
- Need to optimize area calculation
- Two-pointer technique applicable
- Greedy algorithm thinking needed
- Similar to trapping rain water but different goal
Key distinction: Container with water maximizes area between two lines, while trapping rain water calculates total trapped water.
Template (Two Pointers)
function maxAreaTemplate(height: number[]): number {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
maxWater = Math.max(maxWater, width * minHeight);
// Always move pointer with smaller height
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxWater;
}Time and Space Complexity Summary
- Two pointers: O(n) time, O(1) space
- Brute force: O(n²) time, O(1) space
- Optimized two pointers: O(n) time, O(1) space
- K containers: O(n² log n) time, O(n²) space
- 3D version: O(rows² * cols) time, O(1) space
- Circular: O(n²) time, O(1) space
Practice Tips
- Understand formula - Area = width Ă min(height[left], height[right])
- Greedy choice - Always move pointer with smaller height
- Why it works - Moving taller pointer can only decrease area
- Visualization - Draw lines to understand area calculation
- Edge cases - Empty array, two elements, all same height
Common Mistakes
- Wrong formula - Not using min of two heights
- Wrong pointer movement - Moving taller pointer instead of shorter
- Index errors - Off-by-one in width calculation
- Not updating max - Forgetting to update maxWater in each iteration
- Edge cases - Not handling arrays with < 2 elements
Related Concepts
- Two Pointers - Core technique
- Greedy Algorithm - Always move smaller pointer
- Trapping Rain Water - Similar but different problem
- Area Calculation - Mathematical foundation
Container with most water is a classic two-pointer problem. Master the greedy approach of always moving the pointer with smaller height for optimal O(n) solution in coding interviews.