Bird
Raised Fist0
Interview Prepsubsets-combinationsmediumAmazonGoogle

Combination Sum II (No Reuse, Duplicates)

Choose your preparation mode4 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Sort candidates and start recursion

Candidates are sorted to [1,1,2,5,6,7,10] to group duplicates and enable pruning. The initial call to backtrack starts at index 0 with an empty path and target 8.

💡 Sorting is crucial to handle duplicates and prune early, setting the stage for efficient backtracking.
Line:candidates.sort() backtrack(0, [], target)
💡 Sorting groups duplicates and allows pruning when candidates exceed the target.
📊
Combination Sum II (No Reuse, Duplicates) - Watch the Algorithm Execute, Step by Step
Watching this step-by-step recursion tree helps you understand how backtracking explores all unique combinations without repetition or duplicates, which is hard to grasp from code alone.
Step 1/20
·Active fillAnswer cell
enteringstart=0path=[]target=8
Call Path (current → root)
backtrack(0, [], 8)
Remaining
i=0 to 6
enteringstart=1path=[1]target=7
Call Path (current → root)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Remaining
i=1 to 6
Partial: [1]
enteringstart=2path=[1,1]target=6
Call Path (current → root)
backtrack(2, [1,1], 6)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Remaining
i=2 to 6
Partial: [1, 1]
enteringstart=3path=[1,1,2]target=4
Call Path (current → root)
backtrack(3, [1,1,2], 4)
backtrack(2, [1,1], 6)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Remaining
i=3 to 6
Partial: [1, 1, 2]
pruningstart=3path=[1,1,2]target=4
Call Path (current → root)
backtrack(3, [1,1,2], 4)
backtrack(2, [1,1], 6)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Tried
3
Partial: [1, 1, 2]
candidate 5 > target 4
enteringstart=4path=[1,1,5]target=1
Call Path (current → root)
backtrack(4, [1,1,5], 1)
backtrack(2, [1,1], 6)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Remaining
i=4 to 6
Partial: [1, 1, 5]
pruningstart=4path=[1,1,5]target=1
Call Path (current → root)
backtrack(4, [1,1,5], 1)
backtrack(2, [1,1], 6)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Tried
4
Partial: [1, 1, 5]
candidate 6 > target 1
enteringstart=3path=[1,2]target=5
Call Path (current → root)
backtrack(3, [1,2], 5)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Remaining
i=3 to 6
Partial: [1, 2]
enteringstart=4path=[1,2,5]target=0
Call Path (current → root)
backtrack(4, [1,2,5], 0)
backtrack(3, [1,2], 5)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Partial: [1, 2, 5]
backtrackingstart=4path=[1,2,5]target=0
Call Path (current → root)
backtrack(4, [1,2,5], 0)
backtrack(3, [1,2], 5)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Partial: [1, 2, 5]
Found 1: [1,2,5]
pruningstart=3path=[1,2]target=5
Call Path (current → root)
backtrack(3, [1,2], 5)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Tried
3
Partial: [1, 2]
candidate 6 > target 5
Found 1: [1,2,5]
enteringstart=4path=[1,5]target=2
Call Path (current → root)
backtrack(4, [1,5], 2)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Remaining
i=4 to 6
Partial: [1, 5]
Found 1: [1,2,5]
pruningstart=4path=[1,5]target=2
Call Path (current → root)
backtrack(4, [1,5], 2)
backtrack(1, [1], 7)
backtrack(0, [], 8)
Tried
4
Partial: [1, 5]
candidate 6 > target 2
Found 1: [1,2,5]
choosingstart=0path=[]target=8
Call Path (current → root)
backtrack(0, [], 8)
Tried
0
Remaining
123456
Found 1: [1,2,5]
enteringstart=3path=[2]target=6
Call Path (current → root)
backtrack(3, [2], 6)
backtrack(0, [], 8)
Remaining
i=3 to 6
Partial: [2]
Found 1: [1,2,5]
enteringstart=5path=[2,6]target=0
Call Path (current → root)
backtrack(5, [2,6], 0)
backtrack(3, [2], 6)
backtrack(0, [], 8)
Partial: [2, 6]
Found 1: [1,2,5]
backtrackingstart=5path=[2,6]target=0
Call Path (current → root)
backtrack(5, [2,6], 0)
backtrack(3, [2], 6)
backtrack(0, [], 8)
Partial: [2, 6]
Found 2: [1,2,5] [2,6]
enteringstart=7path=[5]target=3
Call Path (current → root)
backtrack(6, [5], 3)
backtrack(0, [], 8)
Partial: [5]
Found 2: [1,2,5] [2,6]
backtrackingstart=7path=[5]target=3
Call Path (current → root)
backtrack(6, [5], 3)
backtrack(0, [], 8)
Partial: [5]
Found 2: [1,2,5] [2,6]
Answer: [[1,1,6],[1,2,5],[1,7],[2,6]]
Total calls: 20 · Pruned: 5

Key Takeaways

Sorting candidates is essential to efficiently skip duplicates and prune the search space.

Without sorting, duplicate skipping and pruning would be much harder to implement and visualize.

Backtracking explores all unique combinations by including or skipping candidates, with pruning to avoid unnecessary work.

Seeing the recursion tree shows how the algorithm branches and prunes, which is not obvious from code alone.

Skipping duplicates at the same recursion level prevents repeated combinations, ensuring uniqueness in the results.

The trace shows exactly when and why duplicates are skipped, clarifying this subtle but critical step.

Practice

(1/5)
1. You are given an array of positive integers and a number k. The task is to determine if the array can be partitioned into k subsets such that the sum of elements in each subset is equal. Which algorithmic approach guarantees an optimal solution for this problem?
easy
A. Dynamic programming based on subset sums without pruning or memoization
B. Greedy algorithm that picks the largest elements first and assigns them to subsets
C. Backtracking with bitmask memoization to explore subsets and prune invalid states
D. Sorting and then using a sliding window to find equal sum partitions

Solution

  1. Step 1: Understand problem constraints and complexity

    The problem requires partitioning into exactly k subsets with equal sums, which is NP-complete and requires exploring combinations exhaustively.
  2. Step 2: Evaluate algorithm suitability

    Greedy and sliding window approaches fail to guarantee correctness because they don't explore all combinations. Simple DP without pruning is inefficient and incomplete. Backtracking with bitmask memoization efficiently prunes and caches states, guaranteeing optimality.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Backtracking with memoization handles all subsets optimally [OK]
Hint: Bitmask memoization prunes repeated subset states [OK]
Common Mistakes:
  • Assuming greedy always works for equal sum partition
  • Confusing subset sum DP with partitioning into k subsets
2. What is the worst-case time complexity of the optimal backtracking solution with sum bound pruning for Combination Sum III (k numbers from 1 to 9 summing to n)?
medium
A. O(k!) because permutations of k numbers are explored
B. O(k * n) because we explore all sums up to n for k elements
C. O(n^k) because we try all sequences of length k with sum n
D. O(2^9) because each number 1-9 can be either included or excluded, pruning reduces calls but worst case remains exponential

Solution

  1. Step 1: Identify search space size

    Numbers 1 to 9 can be included or excluded, so 2^9 subsets possible.
  2. Step 2: Consider pruning effect

    Pruning reduces calls but worst-case complexity remains O(2^9) since all subsets might be explored.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Backtracking worst case is exponential in number of candidates [OK]
Hint: Backtracking subsets of 9 elements is O(2^9) worst case [OK]
Common Mistakes:
  • Confusing with DP complexity O(k*n)
  • Thinking permutations cause factorial complexity
3. The following code attempts to solve the Matchsticks to Square problem using backtracking with bitmask and memoization. Identify the line containing the subtle bug that can cause incorrect results or infinite recursion.
medium
A. Line with 'if total % 4 != 0: return false' (no divisibility check)
B. Line with 'matchsticks.sort(reverse=true)' (missing sorting causes inefficiency)
C. Line with 'memo[(used_mask, curr_sum, sides_formed)] = false' (incorrect memoization)
D. Line with recursive call 'backtrack(next_mask, next_sum, sides_formed)' missing return statement

Solution

  1. Step 1: Identify missing return in recursion

    The recursive call without return means the function ignores successful paths, causing incorrect results or infinite recursion.
  2. Step 2: Confirm other lines are correct

    Divisibility check is present, sorting is done, and memoization line is correct for caching failures.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Missing return in recursive call breaks backtracking correctness [OK]
Hint: Missing return in recursive call breaks correctness [OK]
Common Mistakes:
  • Forgetting to return recursive call result
  • Skipping divisibility check
  • Not sorting before backtracking
4. Examine the following buggy code snippet from the trie-based solution. Which line contains the subtle bug that causes incorrect counts for some puzzles?
medium
A. Line where node.count is incremented
B. Line missing filtering words with more than 7 unique letters before insertion
C. Line checking if bit is set in mask during insertion
D. Line checking if puzzle_mask contains child bit during dfs

Solution

  1. Step 1: Identify filtering step

    The code inserts all words regardless of unique letter count, causing large trie and slow traversal.
  2. Step 2: Understand impact

    Words with >7 unique letters cannot match puzzles (7 letters max), so filtering them out is necessary for correctness and efficiency.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Filtering missing leads to incorrect counts and performance issues [OK]
Hint: Filter words with >7 unique letters before insertion [OK]
Common Mistakes:
  • Not filtering large words
  • Incorrect bit checks
  • Misplaced count increments
5. Suppose you want to generate all combinations of size k from numbers 1 to n, but now elements can be reused multiple times in a combination (combinations with replacement). Which modification to the backtracking approach correctly handles this?
hard
A. Use iterative lexicographic generation without any changes
B. Change the recursive call to backtrack(i + 1, path) to prevent reuse of elements
C. Change the recursive call to backtrack(i, path) to allow reuse of the current element
D. Sort the input array before backtracking to handle duplicates

Solution

  1. Step 1: Understand combinations with replacement allow repeated elements

    To allow reuse, the recursive call must not increment start index beyond current element.
  2. Step 2: Modify recursion to backtrack(i, path) to allow current element reuse

    This ensures the same element can be chosen multiple times in subsequent recursive calls.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Using backtrack(i, path) enables combinations with replacement [OK]
Hint: Reuse requires recursive calls with same start index [OK]
Common Mistakes:
  • Incrementing start index prevents element reuse