Problem overview
The function max_area receives a vector height of non-negative integers. Each number is a vertical line standing at its own position, so the line at index i has height height[i]. Pick two lines. Together with the x-axis they form a container, and the task is to find the largest amount of water any such container can hold. The container can't be tilted, and there are at least two lines.
The water level is limited by the shorter of the two lines, and the width is the distance between their indices. So the area for lines left and right is min(height[left], height[right]) * (right - left). For [1, 8, 6, 2, 5, 4, 8, 3, 7] the best pair is the lines at indices 1 and 8, with heights 8 and 7: min(8, 7) * 7 = 49.
Approach: start wide, move the shorter line
Trying every pair costs O(n²). The two-pointer idea gets it down to O(n) by never looking at a pair that can't win.
Start with the widest container, with left at the first line and right at the last. Compute its area, then shrink the width by one by moving one of the pointers. The key question is which one. Suppose the left line is the shorter. Any container that keeps this left line and uses a closer right line has a smaller width, and its height is still capped by the left line, which is no taller than before. Its area can't be larger than the current one. So the left line is useless in every other pairing, and moving it loses nothing. The same holds for the right line when it is the shorter one. Moving the taller line, by contrast, could throw away the best answer.
Each step discards one line, so the pointers meet after n - 1 steps, and the maximum over all the areas seen is the answer.
Step by step
- Declare the state.
resultis a mutablei32starting at 0, andleftandrightare mutableusizeindices at the two ends ofheight. The last index isheight.len() - 1. - Loop while
left < right. When they meet, no pair of lines is left. - Compute the area. Take the shorter of the two heights with
min, and multiply by the distanceright - left, cast withas i32so it matches thei32heights. The cast applies to the parenthesized distance only, becauseasbinds tighter than*. - Keep the best.
result = result.max(area). Themaxmethod returns the larger of the two values, soresultnever decreases. - Move the left pointer with
left += 1whenheight[left] < height[right]. - Otherwise move the right pointer with
right -= 1. This covers the case where the right line is the shorter, and also ties. With equal heights, moving either pointer is safe, because a container that keeps one of two equal lines and gets narrower can't be larger. - Return
resultas the final expression, without a semicolon.
The solution in Rust
impl Solution {
fn max_area(height: Vec<i32>) -> i32 {
let mut result: i32 = 0;
let mut left: usize = 0;
let mut right: usize = height.len() - 1;
while left < right {
let area = height[left].min(height[right]) * (right - left) as i32;
result = result.max(area);
if height[left] < height[right] {
left += 1;
} else {
right -= 1;
}
}
result
}
}
Rust details worth knowing:
minandmaxare methods. For integers,a.min(b)anda.max(b)come from theOrdtrait. They return a value, so they are used asresult = result.max(area), not as statements that update in place.- Mixed integer types.
right - leftis ausizeand the heights arei32, and Rust never converts between integer types on its own. The explicitas i32is required. The subtraction itself is safe because the loop guaranteesright > left. - Why
rightcan be decremented. In theelsebranch,right > left >= 0, sorightis at least 1. - Indexing panics. Reading
height[left]outside the vector stops the program, rather than returning garbage. Here both pointers stay inside. - Type annotations.
let mut result: i32 = 0;andlet mut left: usize = 0;spell the types out. Rust would infer them, but the annotation documents which integer type each variable has.
Complexity
Time: O(n). Each iteration moves one pointer one step inward, and the pointers start n - 1 apart, so the loop runs exactly n - 1 times with constant work per iteration.
Space: O(1). The function uses three variables and an area.
Edge cases and common mistakes
- Two lines.
[1, 1]gives 1: one iteration, width 1, height 1. - Zero heights.
[0, 0, 0]gives 0, because every container's height is 0. - The best pair isn't the tallest.
[1, 2, 1]gives 2: the two 1s are width 2 apart, and 1 * 2 = 2.[4, 3, 2, 1, 4]gives 16, from the two outer 4s, even though the middle lines are shorter. - Tall lines far apart.
[5, 0, 0, 0, 5]gives 20. The empty space between the lines still counts toward the width. - Using the taller line's height. Water spills over the shorter line, so the height is
min, nevermax. - Moving the wrong pointer. Moving the taller one can only shrink the width without raising the height cap, and it can discard the best container.
- Empty input. The statement guarantees at least two lines. With an empty vector,
height.len() - 1wraps in an optimized build, and the firstheight[left]panics with index out of bounds. - Overflow. The area is computed in
i32, so a very large area wraps silently in an optimized build, and panics in a debug build. For example, 100,000 lines of height 100,000 have a true best area of 9,999,900,000, which doesn't fit in ani32. Ani64would hold it.
Testing
[1, 8, 6, 2, 5, 4, 8, 3, 7]gives 49.[1, 1]gives 1.[4, 3, 2, 1, 4]gives 16.[1, 2, 1]gives 2.[5, 0, 0, 0, 5]gives 20.[1, 2, 3, 4, 5]gives 6.

