Combination Sum medium
Problem Statement
Given an array of distinct integers candidates
and a target integer target
, return a list of all unique combinations of candidates
where the chosen numbers sum to target
. You may return the combinations in any order.
The same number may be chosen from candidates
an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.
It's guaranteed that the number of unique combinations is at most 1000.
Example 1
Input: candidates = [2,3,6,7], target = 7 Output: [[2,2,3],[7]] Explanation: 2 and 3 are candidates. 2 + 2 + 3 = 7. Also 7 is a candidate.
Example 2
Input: candidates = [2,3,5], target = 8 Output: [[2,2,2,2],[2,3,3],[3,5]] Explanation: 2, 3, and 5 are candidates. 2 + 2 + 2 + 2 = 8, 2 + 3 + 3 = 8, 3 + 5 = 8.
Steps
-
Sort the candidates: Sorting allows us to efficiently prune the search space later.
-
Backtracking function: We'll use a recursive backtracking function to explore all possible combinations. This function takes the following arguments:
index
: The current index in thecandidates
array.combination
: The current combination being built.remaining
: The remaining sum needed to reach thetarget
.result
: A list to store the final combinations.
-
Base Cases:
- If
remaining
is 0, we've found a valid combination, so addcombination
toresult
. - If
remaining
is negative orindex
is out of bounds, we've gone beyond the target or exhausted all candidates, so we backtrack.
- If
-
Recursive Step: For each candidate at the current
index
:- Include the candidate: Add it to
combination
, updateremaining
, and recursively call the function with the sameindex
(since we can reuse the same candidate). - Exclude the candidate: Recursively call the function with the next
index
(index + 1
).
- Include the candidate: Add it to
-
Return the result: After exploring all possibilities, return the
result
list.
Explanation
The backtracking approach systematically explores all possible combinations. Sorting the candidates helps to optimize the search by avoiding unnecessary explorations. The recursive calls with the same index allow for the selection of the same number multiple times. The base cases handle the termination conditions efficiently.
Code
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution {
public IList<IList<int>> CombinationSum(int[] candidates, int target) {
Array.Sort(candidates); // Sort for optimization
IList<IList<int>> result = new List<IList<int>>();
Backtrack(candidates, target, 0, new List<int>(), result);
return result;
}
private void Backtrack(int[] candidates, int remaining, int index, List<int> combination, IList<IList<int>> result) {
if (remaining == 0) {
result.Add(new List<int>(combination)); // Add a copy to avoid modification
return;
}
if (remaining < 0 || index >= candidates.Length) {
return; // Base case: exceeded target or exhausted candidates
}
// Include the current candidate
combination.Add(candidates[index]);
Backtrack(candidates, remaining - candidates[index], index, combination, result); // Same index for reuse
// Exclude the current candidate
combination.RemoveAt(combination.Count - 1);
Backtrack(candidates, remaining, index + 1, combination, result); // Move to next candidate
}
}
Complexity
- Time Complexity: O(N^T), where N is the number of candidates and T is the target value. In the worst case, we might explore all possible combinations.
- Space Complexity: O(T) in the worst case, due to the recursive call stack and the maximum depth of the combination list. The space used for the
result
list is proportional to the number of combinations, which is also bounded by O(N^T).