Bird
Raised Fist0
Interview Preptwo-pointersmediumAmazonGoogleFacebookBloomberg

Container With Most Water

Choose your preparation mode4 modes available
🎯
Container With Most Water
mediumTWO_POINTERAmazonGoogleFacebook

Imagine you have vertical lines on a graph representing walls, and you want to find two walls that can hold the most water between them.

💡 This problem is about finding the maximum area between two lines in an array. Beginners often struggle because the naive approach tries all pairs, which is slow, and the optimal two-pointer technique requires understanding how moving pointers affects the area. Think of it like trying to find the widest container between two walls that can hold the most water.
📋
Problem Statement

Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i are at (i, ai) and (i, 0). Find two lines, which together with the x-axis forms a container, such that the container contains the most water. Return the maximum amount of water a container can store.

1 ≤ n ≤ 10^50 ≤ ai ≤ 10^4 for each 1 ≤ i ≤ n
💡
Example
Input"[1,8,6,2,5,4,8,3,7]"
Output49

The max area is formed between lines at index 1 (height 8) and index 8 (height 7). Width is 7, height is min(8,7)=7, area=7*7=49.

  • All heights are zero → max area is 0
  • Only one line → max area is 0 (no container)
  • Two lines with same height → area is height * distance
  • Heights are strictly increasing or decreasing → max area at ends
⚠️
Common Mistakes
Moving the pointer at the taller line instead of the shorter line

Misses the optimal solution, returns incorrect max area

✅ Always move the pointer pointing to the shorter line inward

Calculating area incorrectly by using max height instead of min height

Overestimates area, leading to wrong answers

✅ Use min(height[left], height[right]) to calculate height

Not handling edge cases like single element or all zero heights

Code may crash or return wrong result

✅ Add checks or rely on loop conditions to handle these cases gracefully

Using nested loops for large inputs without optimization

Code times out or is inefficient

✅ Use two-pointer approach to reduce time complexity to O(n)

🧠
Brute Force (Nested Loops)
💡 This approach tries every possible pair of lines to find the maximum area. It is simple and intuitive but inefficient for large inputs. Beginners can understand the problem well by seeing how all pairs are checked.

Intuition

Check all pairs of lines, calculate the area for each, and keep track of the maximum area found.

Algorithm

  1. Initialize max_area to 0.
  2. Use two nested loops to consider every pair of lines (i, j) with i < j.
  3. Calculate the area formed by lines i and j: width = j - i, height = min(height[i], height[j]).
  4. Update max_area if the current area is larger.
💡 The nested loops make it easy to understand but hard to scale; beginners often miss how to calculate area correctly.
</>
Code
def maxArea(height):
    max_area = 0
    n = len(height)
    for i in range(n):
        for j in range(i + 1, n):
            width = j - i
            current_height = min(height[i], height[j])
            area = width * current_height
            if area > max_area:
                max_area = area
    return max_area

# Driver code
if __name__ == '__main__':
    heights = [1,8,6,2,5,4,8,3,7]
    print(maxArea(heights))  # Expected output: 49
Line Notes
max_area = 0Initialize max_area to keep track of the largest area found so far.
for i in range(n)Outer loop picks the first line index.
for j in range(i + 1, n)Inner loop picks the second line index, ensuring pairs are unique and i < j.
area = width * current_heightCalculate the area between lines i and j using width and the smaller height.
public class Solution {
    public static int maxArea(int[] height) {
        int maxArea = 0;
        int n = height.length;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int width = j - i;
                int currentHeight = Math.min(height[i], height[j]);
                int area = width * currentHeight;
                if (area > maxArea) {
                    maxArea = area;
                }
            }
        }
        return maxArea;
    }

    public static void main(String[] args) {
        int[] heights = {1,8,6,2,5,4,8,3,7};
        System.out.println(maxArea(heights)); // Expected output: 49
    }
}
Line Notes
int maxArea = 0;Initialize maxArea to store the maximum area found.
for (int i = 0; i < n; i++)Outer loop selects the first line index.
for (int j = i + 1; j < n; j++)Inner loop selects the second line index, ensuring unique pairs.
int area = width * currentHeight;Calculate the area between two lines.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int maxArea(vector<int>& height) {
    int max_area = 0;
    int n = height.size();
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            int width = j - i;
            int current_height = min(height[i], height[j]);
            int area = width * current_height;
            if (area > max_area) {
                max_area = area;
            }
        }
    }
    return max_area;
}

int main() {
    vector<int> heights = {1,8,6,2,5,4,8,3,7};
    cout << maxArea(heights) << endl; // Expected output: 49
    return 0;
}
Line Notes
int max_area = 0;Initialize max_area to track the largest area found.
for (int i = 0; i < n; i++)Outer loop picks the first line index.
for (int j = i + 1; j < n; j++)Inner loop picks the second line index, ensuring unique pairs.
int area = width * current_height;Calculate area between lines i and j.
function maxArea(height) {
    let maxArea = 0;
    const n = height.length;
    for (let i = 0; i < n; i++) {
        for (let j = i + 1; j < n; j++) {
            const width = j - i;
            const currentHeight = Math.min(height[i], height[j]);
            const area = width * currentHeight;
            if (area > maxArea) {
                maxArea = area;
            }
        }
    }
    return maxArea;
}

// Test
const heights = [1,8,6,2,5,4,8,3,7];
console.log(maxArea(heights)); // Expected output: 49
Line Notes
let maxArea = 0;Initialize maxArea to keep track of the maximum area found.
for (let i = 0; i < n; i++)Outer loop selects the first line index.
for (let j = i + 1; j < n; j++)Inner loop selects the second line index, ensuring unique pairs.
const area = width * currentHeight;Calculate the area between two lines.
⏱
Complexity
TimeO(n^2)
SpaceO(1)

Two nested loops each run up to n times, resulting in n*(n-1)/2 pairs checked.

💡 For n=20, this means about 190 operations; for n=1000, about 500,000 operations, which is slow.
Interview Verdict: TLE for large inputs

This approach is too slow for large inputs but is useful to understand the problem and correctness.

🧠
Two Pointers (Greedy Pointer Movement)
💡 This approach uses two pointers starting at opposite ends and moves them inward to find the maximum area efficiently. It is a classic example of the two-pointer technique that reduces time complexity drastically.

Intuition

Start with the widest container and move the pointer at the shorter line inward, hoping to find a taller line that increases area.

Algorithm

  1. Initialize two pointers: left at 0 and right at n-1.
  2. Calculate area between left and right pointers and update max_area if larger.
  3. Move the pointer pointing to the shorter line inward by one to try to find a taller line.
  4. Repeat until left and right pointers meet.
💡 The key insight is moving the shorter pointer inward because the area is limited by the shorter line.
</>
Code
def maxArea(height):
    left, right = 0, len(height) - 1
    max_area = 0
    while left < right:
        width = right - left
        current_height = min(height[left], height[right])
        area = width * current_height
        if area > max_area:
            max_area = area
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    return max_area

# Driver code
if __name__ == '__main__':
    heights = [1,8,6,2,5,4,8,3,7]
    print(maxArea(heights))  # Expected output: 49
Line Notes
left, right = 0, len(height) - 1Initialize pointers at the array ends to start with the widest container.
while left < right:Loop until pointers meet, checking all possible containers between them.
if height[left] < height[right]:Move the pointer at the shorter line inward to try to find a taller line.
max_area = areaUpdate max_area when a larger area is found.
public class Solution {
    public static int maxArea(int[] height) {
        int left = 0, right = height.length - 1;
        int maxArea = 0;
        while (left < right) {
            int width = right - left;
            int currentHeight = Math.min(height[left], height[right]);
            int area = width * currentHeight;
            if (area > maxArea) {
                maxArea = area;
            }
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return maxArea;
    }

    public static void main(String[] args) {
        int[] heights = {1,8,6,2,5,4,8,3,7};
        System.out.println(maxArea(heights)); // Expected output: 49
    }
}
Line Notes
int left = 0, right = height.length - 1;Initialize two pointers at the start and end of the array.
while (left < right)Loop until pointers cross, checking all containers between them.
if (height[left] < height[right])Move the pointer at the shorter line inward to potentially increase area.
maxArea = area;Update maxArea when a larger area is found.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int maxArea(vector<int>& height) {
    int left = 0, right = height.size() - 1;
    int max_area = 0;
    while (left < right) {
        int width = right - left;
        int current_height = min(height[left], height[right]);
        int area = width * current_height;
        if (area > max_area) {
            max_area = area;
        }
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    return max_area;
}

int main() {
    vector<int> heights = {1,8,6,2,5,4,8,3,7};
    cout << maxArea(heights) << endl; // Expected output: 49
    return 0;
}
Line Notes
int left = 0, right = height.size() - 1;Initialize two pointers at the array ends.
while (left < right)Loop until pointers meet, checking all containers between them.
if (height[left] < height[right])Move the pointer at the shorter line inward to try to find a taller line.
max_area = area;Update max_area when a larger area is found.
function maxArea(height) {
    let left = 0, right = height.length - 1;
    let maxArea = 0;
    while (left < right) {
        const width = right - left;
        const currentHeight = Math.min(height[left], height[right]);
        const area = width * currentHeight;
        if (area > maxArea) {
            maxArea = area;
        }
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    return maxArea;
}

// Test
const heights = [1,8,6,2,5,4,8,3,7];
console.log(maxArea(heights)); // Expected output: 49
Line Notes
let left = 0, right = height.length - 1;Initialize pointers at the start and end of the array.
while (left < right)Loop until pointers meet, checking all possible containers.
if (height[left] < height[right])Move the pointer at the shorter line inward to try to find a taller line.
maxArea = area;Update maxArea when a larger area is found.
⏱
Complexity
TimeO(n)
SpaceO(1)

Each pointer moves at most n times, so total operations are proportional to n.

💡 For n=10^5, this means about 100,000 operations, which is efficient for interviews.
Interview Verdict: Accepted and optimal

This approach is the best balance of simplicity and efficiency for this problem.

🧠
Two Pointers with Early Area Calculation Optimization
💡 This approach is a slight variation of the two-pointer method that emphasizes minimizing redundant calculations by updating max_area only when necessary. It is a micro-optimization that can be useful in some environments.

Intuition

Calculate area only when pointers move and keep track of max_area without recalculating unnecessarily.

Algorithm

  1. Initialize left and right pointers at the ends of the array.
  2. Calculate initial area and set max_area.
  3. While left < right, move the pointer at the shorter line inward.
  4. Calculate new area only after pointer moves and update max_area if larger.
💡 This approach is a micro-optimization that can help in some languages or environments but does not change overall complexity.
</>
Code
def maxArea(height):
    left, right = 0, len(height) - 1
    max_area = (right - left) * min(height[left], height[right])
    while left < right:
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
        width = right - left
        if width <= 0:
            break
        current_height = min(height[left], height[right])
        area = width * current_height
        if area > max_area:
            max_area = area
    return max_area

# Driver code
if __name__ == '__main__':
    heights = [1,8,6,2,5,4,8,3,7]
    print(maxArea(heights))  # Expected output: 49
Line Notes
max_area = (right - left) * min(height[left], height[right])Initialize max_area with the area formed by the outermost lines.
if height[left] < height[right]:Move the pointer at the shorter line inward to try to find a taller line.
width = right - leftCalculate the new width after pointer movement.
if area > max_area:Update max_area only if the new area is larger.
public class Solution {
    public static int maxArea(int[] height) {
        int left = 0, right = height.length - 1;
        int maxArea = (right - left) * Math.min(height[left], height[right]);
        while (left < right) {
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
            int width = right - left;
            if (width <= 0) break;
            int currentHeight = Math.min(height[left], height[right]);
            int area = width * currentHeight;
            if (area > maxArea) {
                maxArea = area;
            }
        }
        return maxArea;
    }

    public static void main(String[] args) {
        int[] heights = {1,8,6,2,5,4,8,3,7};
        System.out.println(maxArea(heights)); // Expected output: 49
    }
}
Line Notes
int maxArea = (right - left) * Math.min(height[left], height[right]);Initialize maxArea with the area of the outermost container.
if (height[left] < height[right])Move the pointer at the shorter line inward.
int width = right - left;Calculate the width after pointer movement.
if (area > maxArea)Update maxArea only if the new area is larger.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int maxArea(vector<int>& height) {
    int left = 0, right = height.size() - 1;
    int max_area = (right - left) * min(height[left], height[right]);
    while (left < right) {
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
        int width = right - left;
        if (width <= 0) break;
        int current_height = min(height[left], height[right]);
        int area = width * current_height;
        if (area > max_area) {
            max_area = area;
        }
    }
    return max_area;
}

int main() {
    vector<int> heights = {1,8,6,2,5,4,8,3,7};
    cout << maxArea(heights) << endl; // Expected output: 49
    return 0;
}
Line Notes
int max_area = (right - left) * min(height[left], height[right]);Initialize max_area with the area of the outermost container.
if (height[left] < height[right])Move the pointer at the shorter line inward.
int width = right - left;Calculate the width after pointer movement.
if (area > max_area)Update max_area only if the new area is larger.
function maxArea(height) {
    let left = 0, right = height.length - 1;
    let maxArea = (right - left) * Math.min(height[left], height[right]);
    while (left < right) {
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
        const width = right - left;
        if (width <= 0) break;
        const currentHeight = Math.min(height[left], height[right]);
        const area = width * currentHeight;
        if (area > maxArea) {
            maxArea = area;
        }
    }
    return maxArea;
}

// Test
const heights = [1,8,6,2,5,4,8,3,7];
console.log(maxArea(heights)); // Expected output: 49
Line Notes
let maxArea = (right - left) * Math.min(height[left], height[right]);Initialize maxArea with the area of the outermost container.
if (height[left] < height[right])Move the pointer at the shorter line inward.
const width = right - left;Calculate the width after pointer movement.
if (area > maxArea)Update maxArea only if the new area is larger.
⏱
Complexity
TimeO(n)
SpaceO(1)

Pointer moves are linear, so time complexity remains O(n).

💡 This approach is as efficient as the standard two-pointer method but with minor calculation optimizations.
Interview Verdict: Accepted and optimal

This approach is a small optimization over the standard two-pointer method but does not change overall complexity.

📊
All Approaches - One-Glance Tradeoffs
💡 The two-pointer approach is the best choice for interviews due to its optimal time and space complexity.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n^2)O(1)NoN/AMention only - never code due to inefficiency
2. Two Pointers (Greedy Pointer Move)O(n)O(1)NoN/ACode this approach for optimal solution
3. Two Pointers with Early Area Calculation OptimizationO(n)O(1)NoN/AOptional micro-optimization, mention if time permits
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and prepare to explain your reasoning clearly in interviews.

How to Present

Step 1: Clarify the problem and constraints with the interviewer.Step 2: Describe the brute force approach and its limitations.Step 3: Introduce the two-pointer technique and explain why it works.Step 4: Code the two-pointer solution carefully.Step 5: Test your code with sample and edge cases.Step 6: Discuss possible optimizations and tradeoffs.

Time Allocation

Clarify: 2min → Approach: 3min → Code: 10min → Test: 5min. Total ~20min

What the Interviewer Tests

The interviewer tests your problem understanding, ability to optimize from brute force to efficient solutions, and your coding accuracy.

Common Follow-ups

  • What if the input array is extremely large? → Use the two-pointer approach for O(n) time.
  • Can you find the container with the second largest area? → Modify the algorithm to track top two areas.
💡 Follow-ups test your ability to adapt the solution and think beyond the basic problem.
🔍
Pattern Recognition

When to Use

1) Problem involves pairs of elements in an array, 2) Need to maximize or minimize a function of two indices, 3) Array is sorted or indices represent positions, 4) Constraints require better than O(n^2) solution.

Signature Phrases

'Find two lines that together with x-axis form a container''Maximize the amount of water contained'

NOT This Pattern When

Problems requiring dynamic programming or sliding window for subarray sums are different patterns.

Similar Problems

Trapping Rain Water - also involves heights and water but requires different approachMax Area of Island - uses DFS but also involves area calculation

Practice

(1/5)
1. Consider the following code snippet for removing duplicates from a sorted array allowing at most two duplicates. What is the value of write_index after processing the input [1,1,2]?
easy
A. 2
B. 1
C. 3
D. 0

Solution

  1. Step 1: Trace write_index and read_index for input [1,1,2]

    Initially, write_index=0. Iteration 0: write_index<2, write 1, write_index=1. Iteration 1: write_index=1<2, write 1, write_index=2. Iteration 2: nums[2]=2 != nums[0]=1, write 2, write_index=3.
  2. Step 2: Final write_index value

    After processing all elements, write_index=3, indicating the length of the modified array.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    All elements allowed, write_index matches array length [OK]
Hint: write_index increments for allowed duplicates [OK]
Common Mistakes:
  • Off-by-one errors in write_index
  • Confusing read_index with write_index
  • Not handling first two elements separately
2. Examine the following code snippet for the 3Sum Smaller problem. It is almost correct but contains one subtle bug. Identify the line with the bug.
medium
A. Line 2: nums.sort()
B. Line 7: if nums[i] + nums[i + 1] + nums[i + 2] >= target:
C. Line 9: while left <= right:
D. Line 12: count += right - left

Solution

  1. Step 1: Understand pointer loop condition

    The two-pointer loop should run while left < right to avoid pointers crossing and invalid indices.
  2. Step 2: Identify bug in loop condition

    Using while left <= right allows left and right to be equal, causing curr_sum to be computed with same index twice, leading to incorrect counts or infinite loops.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Changing to while left < right fixes the bug [OK]
Hint: Two-pointer loops must use strict inequality to avoid overlap [OK]
Common Mistakes:
  • Using <= instead of < in two-pointer loops
  • Forgetting to sort before two pointers
  • Incrementing count incorrectly
3. Consider the following buggy code for merging two sorted arrays in-place. Which line contains the subtle bug that causes missing elements in the final merged array?
def merge(nums1, m, nums2, n):
    i = m - 1
    j = n - 1
    k = m + n - 1
    while i >= 0 and j >= 0:
        if nums1[i] > nums2[j]:
            nums1[k] = nums1[i]
            i -= 1
        else:
            nums1[k] = nums2[j]
            j -= 1
        k -= 1
    # Missing copying remaining nums2 elements

nums1 = [1,2,3,0,0,0]
m = 3
nums2 = [2,5,6]
n = 3
merge(nums1, m, nums2, n)
print(nums1)
medium
A. Line 12: Missing while loop to copy remaining nums2 elements
B. Line 9: nums1[k] = nums2[j]
C. Line 4: i = m - 1
D. Line 7: if nums1[i] > nums2[j]:

Solution

  1. Step 1: Analyze the main merging loop

    The main loop merges elements from the back correctly but stops when either i or j is negative.
  2. Step 2: Identify missing step

    There is no loop copying remaining elements from nums2 if any remain, causing missing elements in nums1.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Without copying leftover nums2 elements, final array is incomplete [OK]
Hint: Always copy leftover nums2 elements after main loop [OK]
Common Mistakes:
  • Forgetting leftover nums2 copy
  • Overwriting nums1 elements too early
4. Suppose the 4Sum problem is modified so that elements can be reused multiple times in quadruplets (i.e., indices can repeat). Which modification to the optimal hash map approach correctly handles this variant?
hard
A. Remove the index overlap check and allow all pairs to combine freely.
B. Use the same hash map approach but skip sorting the array to allow duplicates.
C. Modify the pair combination step to allow pairs with overlapping indices and remove index checks, but ensure quadruplets are sorted and unique.
D. Switch to brute force four nested loops since hash map approach cannot handle reuse.

Solution

  1. Step 1: Understand reuse means indices can repeat

    Index overlap check must be removed or relaxed to allow repeated elements.
  2. Step 2: Adjust pair combination logic

    Allow pairs with overlapping indices but still sort quadruplets and use a set to avoid duplicates.
  3. Step 3: Evaluate other options

    Removing sorting breaks duplicate handling; brute force is inefficient; simply removing checks without sorting leads to duplicates.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    Allow index overlap but keep sorting and deduplication [OK]
Hint: Allow index overlap but keep sorting and deduplication [OK]
Common Mistakes:
  • Removing sorting causes duplicates
  • Assuming brute force is only solution
5. Suppose the input array is no longer sorted but you still need to return the squares of each number in sorted order. Which of the following approaches is correct and efficient?
hard
A. Square each element and then sort the resulting array using a standard sorting algorithm.
B. Use a max-heap to insert squares and extract them in sorted order.
C. Use the two-pointer approach from ends, as it works regardless of input order.
D. Modify the input array in place by squaring and then perform an in-place merge sort.

Solution

  1. Step 1: Recognize input is unsorted

    Two-pointer approach relies on sorted input; it fails if input is unsorted.
  2. Step 2: Choose a correct approach

    Squaring then sorting with a standard sort is correct and efficient (O(n log n)) for unsorted input.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Sorting after squaring handles unsorted input correctly [OK]
Hint: Two-pointer requires sorted input; else sort after squaring [OK]
Common Mistakes:
  • Applying two-pointer on unsorted input
  • Using complex data structures unnecessarily
  • Assuming in-place modifications preserve order
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard