Bird
Raised Fist0
Interview Preptwo-pointersmediumAmazonFacebookGoogleMicrosoftBloomberg

3Sum

Choose your preparation mode4 modes available
▶
Steps
setup

Sort the input array

The input array is sorted to [-4, -1, -1, 0, 1, 2]. Sorting helps easily skip duplicates and enables efficient complement search.

💡 Sorting is crucial because it groups duplicates together and allows the algorithm to avoid repeated triplets.
Line:nums.sort()
💡 Sorted array is the foundation for the two-pointer and hash set approach.
📊
3Sum - Watch the Algorithm Execute, Step by Step
Watching each pointer move and each decision to add or skip elements reveals how the algorithm efficiently finds unique triplets summing to zero without brute force.
Step 1/20
·Active fill★Answer cell
setup
-4
0
-1
1
-1
2
0
3
1
4
2
5
Result: []
setup
i
-4
0
-1
1
-1
2
0
3
1
4
2
5
Result: []
compare
i
-4
0
j
-1
1
-1
2
0
3
1
4
2
5
Result: []
compare
i
-4
0
-1
1
j
-1
2
0
3
1
4
2
5
Result: []
compare
i
-4
0
-1
1
-1
2
j
0
3
1
4
2
5
Result: []
compare
i
-4
0
-1
1
-1
2
0
3
j
1
4
2
5
Result: []
compare
i
-4
0
-1
1
-1
2
0
3
1
4
j
2
5
Result: []
setup
-4
0
i
-1
1
-1
2
0
3
1
4
2
5
Result: []
compare
-4
0
i
-1
1
j
-1
2
0
3
1
4
2
5
Result: []
compare
-4
0
i
-1
1
-1
2
j
0
3
1
4
2
5
Result: []
record
-4
0
i
-1
1
-1
2
0
3
j
1
4
2
5
Result: [[-1,0,1]]
move_right
-4
0
i
-1
1
-1
2
0
3
1
4
j
2
5
Result: [[-1,0,1]]
record
-4
0
i
-1
1
-1
2
0
3
1
4
j
2
5
Result: [[-1,0,1],[-1,-1,2]]
prune
-4
0
-1
1
i
-1
2
0
3
1
4
2
5
Result: [[-1,0,1],[-1,-1,2]]
setup
-4
0
-1
1
-1
2
i
0
3
1
4
2
5
Result: [[-1,0,1],[-1,-1,2]]
compare
-4
0
-1
1
-1
2
i
0
3
j
1
4
2
5
Result: [[-1,0,1],[-1,-1,2]]
compare
-4
0
-1
1
-1
2
i
0
3
1
4
j
2
5
Result: [[-1,0,1],[-1,-1,2]]
setup
-4
0
-1
1
-1
2
0
3
i
1
4
2
5
Result: [[-1,0,1],[-1,-1,2]]
compare
-4
0
-1
1
-1
2
0
3
i
1
4
j
2
5
Result: [[-1,0,1],[-1,-1,2]]
prune
-4
0
-1
1
-1
2
0
3
1
4
i
2
5
Result: [[-1,0,1],[-1,-1,2]]

Key Takeaways

✓ Sorting the array upfront allows efficient duplicate skipping and simplifies complement search.

Without sorting, handling duplicates and searching complements would be more complex and error-prone.

✓ Using a hash set for each fixed element i lets us find pairs summing to -nums[i] in linear time.

This avoids the O(n^2) brute force by quickly checking complements in constant time.

✓ Skipping duplicates for both fixed elements and inner scanning pointers prevents repeated triplets in the result.

This subtle step ensures the output contains unique triplets without extra overhead.

Practice

(1/5)
1. You are given an array of integers and need to move all zero elements to the end while maintaining the relative order of non-zero elements. Which algorithmic approach guarantees an optimal in-place solution with O(n) time and O(1) space complexity?
easy
A. Use an extra array to copy all non-zero elements first, then fill the rest with zeroes.
B. Use a greedy approach that swaps zeroes with the next non-zero element found by scanning from the end.
C. Use dynamic programming to track the minimum swaps needed to move zeroes to the end.
D. Use a two-pointer approach that overwrites non-zero elements at the front and fills zeroes after all non-zero elements.

Solution

  1. Step 1: Understand problem constraints

    The problem requires moving zeroes to the end while preserving the order of non-zero elements, with optimal time and space.
  2. Step 2: Identify the approach that meets constraints

    The two-pointer overwrite approach scans once, writes non-zero elements in order, then fills zeroes, achieving O(n) time and O(1) space.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Two-pointer overwrite is the classic optimal solution [OK]
Hint: Two-pointer overwrite ensures order and in-place operation [OK]
Common Mistakes:
  • Confusing greedy swaps with in-place overwrite
  • Using extra space when not allowed
  • Applying DP unnecessarily
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

  1. Step 1: Analyze second loop behavior

    The second loop assigns nums[i] = nums[i], which does nothing and fails to fill zeroes.
  2. Step 2: Identify correct fix

    It should assign nums[i] = 0 to move zeroes to the end properly.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Zeroes remain unfilled, causing incorrect output [OK]
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. 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

  1. 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.
  2. 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.
  3. Step 3: Why other options fail

    Counting '#' forward is complex and error-prone; stacks use extra space; preprocessing removes needed info and breaks order.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Backward two-pointer approach handles consecutive backspaces efficiently [OK]
Hint: Backward two-pointer skips multiple backspaces efficiently [OK]
Common Mistakes:
  • Trying to count '#' forward complicates logic
  • Using stacks increases space unnecessarily
  • Preprocessing removes order and breaks correctness
4. Suppose the problem changes: you want to count pairs with absolute difference at most K, but now the array can contain duplicate elements and you want to count pairs where elements can be reused multiple times (i.e., pairs (i, j) with i ≤ j). Which modification to the binary search approach correctly handles this variant?
hard
A. Start binary search from i instead of i + 1 and count pairs where j ≥ i, adjusting count accordingly.
B. Use brute force nested loops to count all pairs with i ≤ j and difference ≤ K, as binary search cannot handle reuse.
C. Use two pointers with left starting at 0 and right starting at 0, moving right pointer forward and counting pairs including i = j.
D. Keep the same code but change bisect_right to bisect_left to include duplicates and allow i ≤ j pairs.

Solution

  1. Step 1: Understand reuse and duplicates impact

    Allowing i ≤ j means pairs with the same element twice are valid; binary search must include current index.
  2. Step 2: Modify binary search start index

    Starting bisect_right from i (not i+1) includes pairs where i == j; counting j ≥ i pairs correctly.
  3. Step 3: Adjust counting logic

    Count pairs as j - i + 1 if including i == j, or carefully count pairs with j ≥ i.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Starting search at i and counting pairs with j ≥ i handles reuse correctly [OK]
Hint: Start binary search at i to include pairs with i == j [OK]
Common Mistakes:
  • Using bisect_left instead of bisect_right incorrectly
  • Ignoring pairs where i == j
  • Assuming two pointers cannot handle reuse
5. Suppose the input array can contain any integer values, not just 0, 1, and 2, but you still want to sort it in a single pass using a similar three-pointer approach. Which modification to the Dutch National Flag algorithm is necessary to handle this general case correctly?
hard
A. Use counting sort instead, as three pointers only work for three distinct values
B. Extend the algorithm to dynamically partition into k segments with k pointers
C. Map the values to 0,1,2 first, then apply the original algorithm
D. Use quicksort partitioning with recursive calls instead of pointers

Solution

  1. Step 1: Understand limitation of original algorithm

    The Dutch National Flag algorithm is designed specifically for three distinct values and uses three pointers to partition accordingly.
  2. Step 2: Generalize to k distinct values

    For more than three distinct values, the algorithm must be extended to k pointers or multiple passes to partition into k segments, as a single pass with three pointers cannot correctly sort arbitrary values.
  3. Step 3: Evaluate other options

    Counting sort is two-pass, mapping values is not always feasible, and quicksort is general but not single pass.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Generalizing requires more pointers or passes, not just three pointers [OK]
Hint: Three pointers only for three distinct values [OK]
Common Mistakes:
  • Trying to map values first
  • Using quicksort for single pass
  • Assuming counting sort is single pass
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard