Container With Most Water
Problem
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
Brainstorming
the amount of water we can store is equal to the area of the container which is length into breadth the breadth is the difference between the 2 longest bars at position i and j where i < j so essentially we take the min(height(i),height(j)) x (j-i)
so for the first example it would be min(8,7) x (8-1) which is 49
but its not true for the second case when height = [1,1]
the other approach is when we treat this problem to [[Trapping Rain water (DP and 2 Pointers)]] there we check the max height till i from left and right using Dynamic programming principals and the amount of water that can be held at i is amount(min(max_left_till_i) - min(max_right_till_i)) - height at i
but that won’t work directly here as an example
see [1,8,6,2,5,4,8,3,7] you will notice that 8 is at position 2 as well as position 6 if we apply dp concept of trapping rain water directly then max_left_till_i will pick 8 at position 6 and ignore the position 2 since we have 8 twice.
the best approach here is we get the farthest possible max heights from left and right and then square the minimum from it
so lets say we take an example. = [8,3,2,7] 8 and 7 are the farthest possible max height from each other we take min(8,7) and square it then we get 49 , but this is also not correct
or is it?
okay if take the hint from leet code
- Try to use two-pointers. Set one pointer to the left and one to the right of the array. Always move the pointer that points to the lower line.
- How can you calculate the amount of water at each step?
this is very similar to what we were thinking earlier but
the amount of water at each step can be max height left - max height right - height at i
the two endpoints of the ith line are (i, 0) and (i, height[i]).
does it mean we are not counting height at i at this step?
okay so i made a mistake this the breadth is the difference between the 2 longest bars at position i and j where i < j so essentially we take the min(height(i),height(j)) x (j-i)
so for the first example it would be `min(8,7) x (8-1)` which is 49
but its not true for the second case when height = `[1,1]`
is correct for [1,1] as min(1,1) is 1 and (1-0) the index position will also give 1 so
yeah the formula is
the breadth is the difference between the 2 longest bars at position i and j where i < j so essentially we take the min(height(i),height(j)) x (j-i) combined with this insight the best approach here is we get the farthest possible max heights from left and right and then square the minimum from it using 2 pointers
- 🍅 (pomodoro::WORK) (duration:: 20m) (begin:: 2025-12-08 17:15) - (end:: 2025-12-08 17:35)
Optimal Solution
the brute force algorithm is on point
Your Core Insight (100% Correct):
min(height[i], height[j]) * (j - i)This is the formula for the area between any two lines i and j.
height = [1,8,6,2,5,4,8,3,7]
max_area = 0
for i in range(len(height)):
for j in range(i+1,len(height)):
current_area = min(height[i], height[j]) * (j-i)
max_area = max(max_area, current_area)
print(max_area)
Your “Aha!” Moment (The Path to a Better Solution):
You combined two ideas:
- “…get the farthest possible max heights from left and right…”
- “…using 2 pointers.”
This is the key. Let’s start with the widest possible container.
- left pointer at index 0.
- right pointer at index len(height) - 1.
This gives us the maximum possible width (j - i). We can calculate an area for this container. Now, we need to make the container smaller to see if we can find a better area.
This leads to the crucial “self-nudge” question.
Question 1: The Greedy Choice
We have to move one of the pointers inward (left++ or right–). This will always decrease the width.
Since the width is guaranteed to shrink, the only way we can possibly find a larger area is if we can increase the height of our container (min(height[left], height[right]))
Let’s say height[left] = 3 and height[right] = 8. The current limiting height is 3.
Choice A: Move the right pointer inward. The new right pointer might be shorter or taller than 8, but our height is still limited by the left pointer’s height of 3. We haven’t improved our chances of getting a taller container.
Choice B: Move the left pointer inward. This is the pointer pointing to the shorter line. By moving it, we are giving up the short line in the hope that the next line
(height[left+1])is taller. This is the only move that has any potential to increase the min height of our container.
That is exactly the correct greedy rule.
“if
height[left] < height[right]then move left else move right”
You’ve captured the core of the O(N) algorithm.
Now, let’s address your excellent follow-up question:
What about equal height?
if height[left] == height[right]
Let’s think it through. Say both are height 5. The current area is 5 * width.
The current limiting height is 5.
To have any chance of finding a larger area, you need to find a pair of lines with a height greater than 5.
- If you move left: You are giving up the left 5. The new container will be
(height[left+1], height[right]=5). - If you move right: You are giving up the right 5. The new container will be
(height[left]=5, height[right-1]).
Can either of these new containers possibly have a height greater than 5? No.
- In the first case, the height will be
min(height[left+1], 5). This can be at most 5. - In the second case, the height will be
min(5, height[right-1]). This can be at most 5.
Since the height is at most 5 and the width is definitely smaller, you are guaranteed that you cannot find a larger area by moving just one pointer. To find a potentially larger area, you must find two new lines that are both taller than 5. To do that, you must discard both of the current lines.
So, when height[left] == height[right], you have a choice. Does it matter which one you move?
Let’s say you move left. The new container is (height[left+1], height[right]). The height is now limited by height[right] (the 5), so the max possible height is still 5. Since the width is smaller, the area will be smaller.
Let’s say you move right. The new container is (height[left], height[right-1]). The height is now limited by height[left] (the 5), so the max possible height is still 5. Since the width is smaller, the area will be smaller.
Conclusion: When the heights are equal, it does not matter which pointer you move. Moving either one guarantees that you cannot find a larger area with the remaining pointer. You can safely move left, or right, or even both. The simplest code results from just picking one.
Your if height[left] < height[right]: left += 1 else: right -= 1 rule handles this perfectly! If they are equal, the else block will run, and right will be moved, which is a valid choice.
height = [1,8,6,2,5,4,8,3,7]
max_area = 0
left = 0
right = len(height) -1
while left < right:
current_area = min(height[left], height[right]) * (right-left)
max_area = max(max_area, current_area)
if height[left] < height[right]: # left height is less than right height then move the left forward
left +=1
else:
right -=1
print(max_area)