Taro Logo

Container With Most Water

Medium
2 months ago

Maximum Area Container

You are given an integer array height of length n. There are n vertical lines drawn such that the two endpoints of the ith line are (i, 0) and (i, height[i]).

Find two lines that together with the x-axis form a container, such that the container contains the most water.

Return the maximum amount of water a container can store.

Notice that you may not slant the container.

Example 1:

Input: height = [1,8,6,2,5,4,8,3,7]
Output: 49
Explanation: The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

Example 2:

Input: height = [1,1]
Output: 1

Constraints:

  • n == height.length
  • 2 <= n <= 10^5
  • 0 <= height[i] <= 10^4

Solve this problem efficiently. What is the time and space complexity of your solution? Can you handle edge cases gracefully (e.g., empty array, array with one element, etc.)?

Sample Answer
# Maximum Area Container Problem

This problem asks us to find the maximum amount of water a container can store, given an array of heights representing vertical lines. The container is formed by two lines and the x-axis. We cannot slant the container.

## Naive Solution

The brute-force approach is to check every possible pair of lines and calculate the area formed by each pair. We can do this by iterating through all possible pairs of indices (i, j) in the `height` array and calculating the area as `min(height[i], height[j]) * (j - i)`.  We then keep track of the maximum area found so far.

```python
def max_area_naive(height):
    max_area = 0
    for i in range(len(height)):
        for j in range(i + 1, len(height)):
            area = min(height[i], height[j]) * (j - i)
            max_area = max(max_area, area)
    return max_area

# Example usage
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
print(f"Maximum area (naive): {max_area_naive(height)}")  # Output: 49

Optimal Solution

A more efficient approach is to use the two-pointer technique. We initialize two pointers, left and right, at the beginning and end of the height array, respectively. In each iteration, we calculate the area formed by the lines at left and right as min(height[left], height[right]) * (right - left). We update the maximum area found so far. Then, we move the pointer pointing to the shorter line towards the center. This is because moving the pointer pointing to the taller line will always result in a smaller area (since the width decreases and the height is limited by the shorter line).

def max_area_optimal(height):
    left = 0
    right = len(height) - 1
    max_area = 0

    while left < right:
        area = min(height[left], height[right]) * (right - left)
        max_area = max(max_area, area)

        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_area

# Example usage
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
print(f"Maximum area (optimal): {max_area_optimal(height)}")  # Output: 49

Big(O) Run-time Analysis

  • Naive Solution: The naive solution has two nested loops, each iterating up to n times, where n is the length of the height array. Therefore, the time complexity is O(n^2).
  • Optimal Solution: The optimal solution uses two pointers, each moving towards the center of the array. In each iteration, we perform a constant amount of work. The while loop iterates at most n times. Therefore, the time complexity is O(n).

Big(O) Space Usage Analysis

  • Naive Solution: The naive solution uses only a few variables to store the maximum area and loop indices. Therefore, the space complexity is O(1).
  • Optimal Solution: The optimal solution uses only a few variables to store the left and right pointers, the maximum area, and the current area. Therefore, the space complexity is O(1).

Edge Cases

  1. Empty array: If the input array is empty, we should return 0.
  2. Array with only one element: If the input array has only one element, we cannot form a container, so we should return 0.
  3. Array with two elements: If the array has two elements, the area is simply min(height[0], height[1]) * 1.
  4. Array with all elements being 0: In this case, the maximum area is 0.
  5. Array with duplicate heights: The algorithm should handle duplicate heights correctly.

Here's an updated version of the optimal solution that explicitly handles edge cases:

def max_area_optimal_with_edge_cases(height):
    if not height or len(height) < 2:
        return 0

    left = 0
    right = len(height) - 1
    max_area = 0

    while left < right:
        area = min(height[left], height[right]) * (right - left)
        max_area = max(max_area, area)

        if height[left] < height[right]:
            left += 1
        else:
            right -= 1

    return max_area

# Example usage with edge cases
height1 = []
height2 = [5]
height3 = [3, 7]
height4 = [0, 0, 0]
height5 = [1, 8, 6, 2, 5, 4, 8, 3, 7]

print(f"Maximum area (edge case 1): {max_area_optimal_with_edge_cases(height1)}")  # Output: 0
print(f"Maximum area (edge case 2): {max_area_optimal_with_edge_cases(height2)}")  # Output: 0
print(f"Maximum area (edge case 3): {max_area_optimal_with_edge_cases(height3)}")  # Output: 3
print(f"Maximum area (edge case 4): {max_area_optimal_with_edge_cases(height4)}")  # Output: 0
print(f"Maximum area (edge case 5): {max_area_optimal_with_edge_cases(height5)}")  # Output: 49