💡 Initial max_area is based on the widest container formed by the outermost lines.
compare
Compare heights at pointers and decide which pointer to move
Compare height[left]=1 and height[right]=7. Since height[left] < height[right], move left pointer inward by 1.
💡 Moving the pointer at the shorter line might find a taller line and potentially a larger area.
Line:if height[left] < height[right]:
left += 1
💡 The shorter line limits the container height, so moving it inward may increase area.
compare
Calculate area with updated pointers
Calculate width = right - left = 8 - 1 = 7. Calculate height = min(height[left], height[right]) = min(8,7) = 7. Calculate area = 7 * 7 = 49. Update max_area to 49 since 49 > 7.
💡 After moving a pointer, recalculate area to check if a larger container is found.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 A larger area is found by moving the left pointer inward to a taller line.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=7. Since height[left] >= height[right], move right pointer inward by 1.
💡 When the right line is shorter or equal, moving it inward may find a taller line and larger area.
Line:else:
right -= 1
💡 Moving the pointer at the shorter or equal height line is the key strategy.
compare
Calculate area with updated pointers
Calculate width = right - left = 7 - 1 = 6. Calculate height = min(height[left], height[right]) = min(8,3) = 3. Calculate area = 6 * 3 = 18. max_area remains 49 since 18 < 49.
💡 Calculate area after pointer moves to check if max_area needs updating.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 Not every pointer move results in a larger area, but all must be checked.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=3. Since height[left] > height[right], move right pointer inward by 1.
💡 Continue moving the pointer at the shorter line to find potentially taller lines.
Line:else:
right -= 1
💡 Pointer moves are always towards the center, focusing on the shorter line.
compare
Calculate area with updated pointers
Calculate width = right - left = 6 - 1 = 5. Calculate height = min(height[left], height[right]) = min(8,8) = 8. Calculate area = 5 * 8 = 40. max_area remains 49 since 40 < 49.
💡 Calculate area after pointer moves to check if max_area needs updating.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 Even with equal heights, the narrower width can reduce area.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=8. Since height[left] >= height[right], move right pointer inward by 1.
💡 When heights are equal, moving either pointer is valid; here we move right pointer.
Line:else:
right -= 1
💡 Moving pointers inward continues to explore all possible containers.
compare
Calculate area with updated pointers
Calculate width = right - left = 5 - 1 = 4. Calculate height = min(height[left], height[right]) = min(8,4) = 4. Calculate area = 4 * 4 = 16. max_area remains 49 since 16 < 49.
💡 Calculate area after pointer moves to check if max_area needs updating.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 Pointer moves can reduce width and height, lowering area.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=4. Since height[left] > height[right], move right pointer inward by 1.
💡 Continue moving the pointer at the shorter line to find potentially taller lines.
Line:else:
right -= 1
💡 Pointer moves focus on the shorter line to maximize area potential.
compare
Calculate area with updated pointers
Calculate width = right - left = 4 - 1 = 3. Calculate height = min(height[left], height[right]) = min(8,5) = 5. Calculate area = 3 * 5 = 15. max_area remains 49 since 15 < 49.
💡 Calculate area after pointer moves to check if max_area needs updating.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 Area can decrease as width shrinks faster than height grows.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=5. Since height[left] > height[right], move right pointer inward by 1.
💡 Continue moving the pointer at the shorter line to find potentially taller lines.
Line:else:
right -= 1
💡 Pointer moves focus on the shorter line to maximize area potential.
compare
Calculate area with updated pointers
Calculate width = right - left = 3 - 1 = 2. Calculate height = min(height[left], height[right]) = min(8,2) = 2. Calculate area = 2 * 2 = 4. max_area remains 49 since 4 < 49.
💡 Calculate area after pointer moves to check if max_area needs updating.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 Area decreases as width shrinks and height is low.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=2. Since height[left] > height[right], move right pointer inward by 1.
💡 Continue moving the pointer at the shorter line to find potentially taller lines.
Line:else:
right -= 1
💡 Pointer moves focus on the shorter line to maximize area potential.
compare
Calculate area with updated pointers
Calculate width = right - left = 2 - 1 = 1. Calculate height = min(height[left], height[right]) = min(8,6) = 6. Calculate area = 1 * 6 = 6. max_area remains 49 since 6 < 49.
💡 Calculate area after pointer moves to check if max_area needs updating.
Line:width = right - left
current_height = min(height[left], height[right])
area = width * current_height
if area > max_area:
max_area = area
💡 Area shrinks as pointers get very close.
compare
Compare heights and move pointer
Compare height[left]=8 and height[right]=6. Since height[left] >= height[right], move right pointer inward by 1.
💡 Continue moving the pointer at the shorter or equal line.
Line:else:
right -= 1
💡 Pointers have now met; the loop will terminate.
prune
Termination and return max_area
Since left pointer is no longer less than right pointer, the loop terminates. Return max_area which is 49.
💡 The algorithm ends when pointers meet or cross, ensuring all pairs have been considered.
Line:while left < right:
...
return max_area
💡 The maximum area found is the final answer.
def maxArea(height):
left, right = 0, len(height) - 1 # STEP 1
max_area = (right - left) * min(height[left], height[right]) # STEP 1
while left < right: # STEP 17 loop condition
if height[left] < height[right]: # STEP 2,4,6,8,10,12,14,16
left += 1 # STEP 2,16
else:
right -= 1 # STEP 4,6,8,10,12,14,16
width = right - left # STEP 3,5,7,9,11,13,15
if width <= 0:
break
current_height = min(height[left], height[right]) # STEP 3,5,7,9,11,13,15
area = width * current_height # STEP 3,5,7,9,11,13,15
if area > max_area: # STEP 3
max_area = area # STEP 3
return max_area # STEP 17
if __name__ == '__main__':
heights = [1,8,6,2,5,4,8,3,7]
print(maxArea(heights)) # Expected output: 49
📊
Container With Most Water - Watch the Algorithm Execute, Step by Step
Watching the pointers move and areas update visually helps you understand why moving the shorter line pointer is optimal and how the maximum area is found efficiently.
Step 1/17
·Active fill★Answer cell
compare
left
1
0
8
1
6
2
2
3
5
4
4
5
8
6
3
7
right
7
8
Result: 7
move_left
1
0
left
8
1
6
2
2
3
5
4
4
5
8
6
3
7
right
7
8
Result: 7
record
1
0
left
8
1
6
2
2
3
5
4
4
5
8
6
3
7
right
7
8
Result: 49
move_right
1
0
left
8
1
6
2
2
3
5
4
4
5
8
6
right
3
7
7
8
Result: 49
record
1
0
left
8
1
6
2
2
3
5
4
4
5
8
6
right
3
7
7
8
Result: 49
move_right
1
0
left
8
1
6
2
2
3
5
4
4
5
right
8
6
3
7
7
8
Result: 49
record
1
0
left
8
1
6
2
2
3
5
4
4
5
right
8
6
3
7
7
8
Result: 49
move_right
1
0
left
8
1
6
2
2
3
5
4
right
4
5
8
6
3
7
7
8
Result: 49
record
1
0
left
8
1
6
2
2
3
5
4
right
4
5
8
6
3
7
7
8
Result: 49
move_right
1
0
left
8
1
6
2
2
3
right
5
4
4
5
8
6
3
7
7
8
Result: 49
record
1
0
left
8
1
6
2
2
3
right
5
4
4
5
8
6
3
7
7
8
Result: 49
move_right
1
0
left
8
1
6
2
right
2
3
5
4
4
5
8
6
3
7
7
8
Result: 49
record
1
0
left
8
1
6
2
right
2
3
5
4
4
5
8
6
3
7
7
8
Result: 49
move_right
1
0
left
8
1
right
6
2
2
3
5
4
4
5
8
6
3
7
7
8
Result: 49
record
1
0
left
8
1
right
6
2
2
3
5
4
4
5
8
6
3
7
7
8
Result: 49
move_right
1
0
right
8
1
6
2
2
3
5
4
4
5
8
6
3
7
7
8
Result: 49
prune
1
0
right
8
1
6
2
2
3
5
4
4
5
8
6
3
7
7
8
Result: 49
Key Takeaways
✓ Moving the pointer at the shorter line is the key to efficiently finding the maximum area.
This insight is hard to see from code alone because it requires understanding why moving the shorter line can lead to a larger area, which is clearer when watching pointer movements.
✓ The maximum area is not always formed by the widest container but by a balance of width and height.
Visualizing the area calculations at each step helps understand how height and width trade off to maximize area.
✓ The algorithm terminates when pointers meet, ensuring all relevant pairs are considered without redundant checks.
Seeing pointers converge step-by-step clarifies the loop termination condition and why no pairs are missed.
Practice
(1/5)
1. Consider the following Python code implementing the optimal two-pointer forward approach for backspace string compare. What is the value of the variable write after processing the input string S = "ab#c" inside the build function?
easy
A. 2
B. 3
C. 4
D. 1
Solution
Step 1: Trace the build function with input "ab#c"
After processing, write=2, indicating the final string length is 2 ("ac").
Final Answer:
Option A -> Option A
Quick Check:
write increments and decrements correctly for backspace [OK]
Hint: write increments on char, decrements on backspace if possible [OK]
Common Mistakes:
Counting '#' as a character and incrementing write
Not decrementing write on backspace
Off-by-one errors in write pointer
2. Consider the following Python code for squaring and sorting an array in-place:
def sortedSquares(nums):
for i in range(len(nums)):
nums[i] = nums[i] * nums[i]
nums.sort()
return nums
nums = [-3, 0, 2]
print(sortedSquares(nums))
What is the output of this code?
easy
A. [9, 4, 0]
B. [9, 0, 4]
C. [0, 4, 9]
D. [4, 0, 9]
Solution
Step 1: Square each element
nums becomes [9, 0, 4] after squaring -3, 0, and 2 respectively.
Step 2: Sort the squared array
Sorting [9, 0, 4] results in [0, 4, 9].
Final Answer:
Option C -> Option C
Quick Check:
Squares then sort yields sorted squares [OK]
Hint: Square then sort yields sorted squares [OK]
Common Mistakes:
Forgetting to sort after squaring
Mixing up order of squared elements
Off-by-one errors in indexing
3. You are given a sorted array of integers and a target sum. You need to find two distinct elements whose sum equals the target. Which approach guarantees an optimal solution in terms of time complexity and correctness?
easy
A. Use two pointers starting at opposite ends of the array, moving inward based on the sum comparison.
B. Use a hash map to store elements and check complements in a single pass.
C. Use dynamic programming to store sums of all pairs and check for the target.
D. Use a greedy approach by iterating from the start and picking the first pair that sums to the target.
Solution
Step 1: Understand the problem constraints
The array is sorted, so two pointers at opposite ends can efficiently find pairs by comparing sums.
Step 2: Evaluate each approach
Greedy fails because it may miss pairs; DP is unnecessary overhead; hash map works but uses extra space and is not optimal for sorted arrays.
Final Answer:
Option A -> Option A
Quick Check:
Two pointers exploit sorting for O(n) time and O(1) space [OK]
4. The following code attempts to move zeroes to the end of the array in-place:
def moveZeroes(nums):
lastNonZeroFoundAt = 0
for num in nums:
if num != 0:
nums[lastNonZeroFoundAt] = num
lastNonZeroFoundAt += 1
# Bug introduced here
for i in range(lastNonZeroFoundAt, len(nums)):
nums[i] = nums[i]
What is the bug in this code?
medium
A. The variable lastNonZeroFoundAt is incremented incorrectly.
B. The first loop does not correctly identify non-zero elements.
C. The second loop does not fill zeroes after lastNonZeroFoundAt, leaving old values intact.
D. The assignment inside the first loop overwrites zeroes incorrectly.
Solution
Step 1: Analyze second loop behavior
The second loop assigns nums[i] = nums[i], which does nothing and fails to fill zeroes.
Step 2: Identify correct fix
It should assign nums[i] = 0 to move zeroes to the end properly.
Hint: Second loop must fill zeroes, not copy existing values [OK]
Common Mistakes:
Forgetting to fill zeroes after moving non-zero elements
Misunderstanding loop purpose
5. If the problem changes so that the same element can be used multiple times to reach the target sum (i.e., elements can be reused), which approach correctly solves the problem?
hard
A. Use the two pointers approach moving inward, but allow pointers to stay if sum < target to reuse elements.
B. Use a modified binary search that allows searching from the current index (not i+1) to reuse elements.
C. Use a dynamic programming approach to find combinations allowing repeated elements.
D. Use the brute force nested loops but break early when sum exceeds target.
Solution
Step 1: Understand reuse requirement
Allowing reuse means pairs can include the same element multiple times, which breaks two-pointer and binary search assumptions.
Step 2: Identify suitable approach
Dynamic programming can handle combinations with repeated elements efficiently, unlike two pointers or binary search.
Final Answer:
Option C -> Option C
Quick Check:
DP handles repeated elements and target sums correctly [OK]
Hint: Reuse -> DP for combinations, not two pointers or binary search [OK]
Common Mistakes:
Trying to reuse pointers incorrectly
Modifying binary search without correctness
Assuming brute force is efficient
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.