💡 Tracking closest sum ensures we remember the best answer found.
shrink
Since current_sum > target, move right pointer left
Because current_sum (2) > target (1), move right pointer from 3 to 2 to try smaller sums.
💡 Moving right pointer left decreases the sum to approach the target.
Line:else:
right -= 1
💡 Pointer movement is guided by sum comparison to target for efficient search.
traverse
End inner loop for i=1 as left >= right
Since left pointer (2) is not less than right pointer (2), inner while loop ends for i=1.
💡 All pairs for fixed i=1 have been checked.
Line:while left < right:
💡 Finished exploring pairs for this fixed element.
reconstruct
Return closest sum 2 as final answer
All triplets checked; return closest_sum = 2 as the sum closest to target 1.
💡 The algorithm finishes by returning the best sum found.
Line:return closest_sum
💡 The closest sum found is the final answer to the problem.
def threeSumClosest(nums, target):
nums.sort() # STEP 1
n = len(nums)
closest_sum = nums[0] + nums[1] + nums[2] # STEP 2
for i in range(n - 2): # STEP 3, 11
left, right = i + 1, n - 1
while left < right: # STEP 10, 15
current_sum = nums[i] + nums[left] + nums[right] # STEP 4, 7, 12
if current_sum == target:
return current_sum
if abs(current_sum - target) < abs(closest_sum - target): # STEP 5, 8, 13
closest_sum = current_sum
if current_sum < target: # STEP 6, 9
left += 1
else: # STEP 14
right -= 1
return closest_sum # STEP 16
📊
3Sum Closest - Watch the Algorithm Execute, Step by Step
Watching each pointer move and each sum comparison helps you understand how the algorithm narrows down to the closest sum efficiently without checking all triplets.
Step 1/16
·Active fill★Answer cell
move_right
-4
0
-1
1
1
2
2
3
record
-4
0
-1
1
1
2
2
3
Result: -4
compare
i
-4
0
left
-1
1
1
2
right
2
3
Result: -4
compare
i
-4
0
left
-1
1
1
2
right
2
3
Result: -4
record
i
-4
0
left
-1
1
1
2
right
2
3
Result: -3
move_left
i
-4
0
-1
1
left
1
2
right
2
3
Result: -3
compare
i
-4
0
-1
1
left
1
2
right
2
3
Result: -3
record
i
-4
0
-1
1
left
1
2
right
2
3
Result: -1
move_left
i
-4
0
-1
1
1
2
right
2
3
Result: -1
traverse
i
-4
0
-1
1
1
2
right
2
3
Result: -1
traverse
-4
0
i
-1
1
left
1
2
right
2
3
Result: -1
compare
-4
0
i
-1
1
left
1
2
right
2
3
Result: -1
record
-4
0
i
-1
1
left
1
2
right
2
3
Result: 2
move_right
-4
0
i
-1
1
right
1
2
2
3
Result: 2
traverse
-4
0
i
-1
1
right
1
2
2
3
Result: 2
record
-4
0
-1
1
1
2
2
3
Result: 2
Key Takeaways
✓ Sorting the array is crucial to enable the two-pointer technique for efficient sum exploration.
Without sorting, we cannot reliably move pointers to increase or decrease sums.
✓ The two-pointer approach reduces the problem from O(n^3) to O(n^2) by smartly moving pointers based on sum comparisons.
This visualizes how pointer movements correspond to increasing or decreasing sums.
✓ Updating the closest sum dynamically ensures we always remember the best candidate without checking all triplets.
Seeing the closest_sum update step-by-step clarifies why early returns or pointer moves are made.
Practice
(1/5)
1. Consider the following Python function that finds the minimum difference between k closest elements in an array. What is the returned value when arr = [4, 1, 7, 9] and k = 2?
easy
A. 2
B. 3
C. 1
D. 0
Solution
Step 1: Sort the array
Sorted array is [1, 4, 7, 9].
Step 2: Slide window of size 2 and compute differences
Check carefully: differences are 3, 3, and 2. Minimum is 2, so answer is 2.
Final Answer:
Option C -> Option C
Quick Check:
Minimum difference between any two sorted elements is 2 [OK]
Hint: Sort array, check adjacent pairs for min difference [OK]
Common Mistakes:
Off-by-one error in window boundaries
Confusing difference calculation with max-min of unsorted subset
Returning 0 prematurely without checking all windows
2. 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
3. Consider the following buggy code for Two Sum II using binary search. Which line contains the subtle bug that may cause incorrect results?
medium
A. Line checking if j != -1
B. Line calling binary_search with left=0 instead of i+1
C. Line returning [i + 1, j + 1]
D. Line calculating complement = target - numbers[i]
Solution
Step 1: Identify binary search call parameters
Binary search should start from i+1 to avoid reusing the same element or previous elements.
Step 2: Analyze impact of starting from 0
Starting from 0 may find the same element at index i, causing incorrect pairs or duplicates.
Final Answer:
Option B -> Option B
Quick Check:
Correct binary search range is i+1 to n-1, not 0 to n-1 [OK]
Hint: Binary search must exclude current index to avoid duplicates [OK]
Common Mistakes:
Searching entire array instead of subarray
Off-by-one in indices
Incorrect complement calculation
4. Examine the following code snippet intended to check if a string is a valid palindrome ignoring case and non-alphanumeric characters. Which line contains a subtle bug that can cause incorrect results on inputs like "A man, a plan, a canal: Panama"?
def isPalindrome(s: str) -> bool:
cleaned = [ch for ch in s if ch.isalnum()]
left, right = 0, len(cleaned) - 1
while left < right:
if cleaned[left].lower() != cleaned[right].lower():
return False
left += 1
right -= 1
return True
medium
A. Line 5: if cleaned[left].lower() != cleaned[right].lower(): (case normalization done here)
B. Line 2: cleaned = [ch for ch in s if ch.isalnum()] (missing lowercasing during cleaning)
C. Line 4: while left < right: (correct loop condition)
D. Line 7: right -= 1 (correct pointer decrement)
Solution
Step 1: Identify where case normalization occurs
Case normalization is done inside the loop condition, not during cleaning.
Step 2: Understand impact of missing lowercasing in cleaning
Since cleaned list stores original case, repeated .lower() calls in loop are inefficient but correct; however, this is not a bug causing incorrect results.
Step 3: Re-examine line 2
Line 2 does not lowercase characters, but line 5 does, so no bug here.
Step 4: Check for subtle bug
Actually, no bug in case normalization, but if line 2 did not skip non-alphanumeric characters, that would cause errors. Here, line 2 correctly filters non-alnum.
Step 5: Reconsider options
None of the lines have a bug causing incorrect results on the example input.
Final Answer:
Option B -> Option B
Quick Check:
Missing lowercasing in cleaning is not a bug since it's done later [OK]
Hint: Case normalization can be done either during cleaning or comparison [OK]
Common Mistakes:
Not skipping non-alphanumeric characters
Not normalizing case consistently
Off-by-one errors in pointer movement
5. Suppose the problem is extended so that the backspace character '#' can be repeated multiple times consecutively, and you want to optimize the algorithm to handle very long sequences of '#' efficiently. Which modification to the two-pointer forward approach is most appropriate?
hard
A. Process the strings from the end with two pointers, skipping characters affected by consecutive backspaces on the fly.
B. Modify the algorithm to skip over consecutive '#' characters by counting them and adjusting the write pointer accordingly in one step.
C. Use a stack to push characters and pop for each '#' to handle multiple backspaces efficiently.
D. Preprocess the strings by removing all '#' characters before comparison.
Solution
Step 1: Understand the challenge with consecutive backspaces
Multiple consecutive '#' characters can cause multiple deletions, which can be inefficient if processed one by one in a forward pass.
Step 2: Identify the best approach
Processing from the end with two pointers allows skipping over characters affected by multiple backspaces efficiently without reconstructing strings or counting '#' explicitly.
Step 3: Why other options fail
Counting '#' forward is complex and error-prone; stacks use extra space; preprocessing removes needed info and breaks order.