Problem Description
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.
Examples
Example 1: Input: candidates = [2,3,6,7], target = 7 Output: [[2,2,3],[7]] Explanation: 2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times. 7 is a candidate, and 7 = 7. These are the only two combinations. Example 2: Input: candidates = [2,3,5], target = 8 Output: [[2,2,2,2],[2,3,3],[3,5]] Example 3: Input: candidates = [2], target = 1 Output: []
Python Solution
def combinationSum(candidates: List[int], target: int) -> List[List[int]]:
def backtrack(remain: int, start: int, path: List[int]):
if remain == 0:
result.append(path[:])
return
for i in range(start, len(candidates)):
if candidates[i] > remain:
continue
path.append(candidates[i])
backtrack(remain - candidates[i], i, path)
path.pop()
result = []
candidates.sort() # Sort to optimize
backtrack(target, 0, [])
return result
Java Solution
class Solution {
public List> combinationSum(int[] candidates, int target) {
List> result = new ArrayList<>();
Arrays.sort(candidates); // Sort to optimize
backtrack(candidates, target, 0, new ArrayList<>(), result);
return result;
}
private void backtrack(int[] candidates, int remain, int start,
List path, List> result) {
if (remain == 0) {
result.add(new ArrayList<>(path));
return;
}
for (int i = start; i < candidates.length; i++) {
if (candidates[i] > remain) {
break; // Stop if we exceed the target
}
path.add(candidates[i]);
backtrack(candidates, remain - candidates[i], i, path, result);
path.remove(path.size() - 1);
}
}
}
C++ Solution
class Solution {
public:
vector> combinationSum(vector& candidates, int target) {
vector> result;
vector path;
sort(candidates.begin(), candidates.end()); // Sort to optimize
backtrack(candidates, target, 0, path, result);
return result;
}
private:
void backtrack(vector& candidates, int remain, int start,
vector& path, vector>& result) {
if (remain == 0) {
result.push_back(path);
return;
}
for (int i = start; i < candidates.size(); i++) {
if (candidates[i] > remain) {
break; // Stop if we exceed the target
}
path.push_back(candidates[i]);
backtrack(candidates, remain - candidates[i], i, path, result);
path.pop_back();
}
}
};
JavaScript Solution
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum = function(candidates, target) {
const result = [];
candidates.sort((a, b) => a - b); // Sort to optimize
const backtrack = (remain, start, path) => {
if (remain === 0) {
result.push([...path]);
return;
}
for (let i = start; i < candidates.length; i++) {
if (candidates[i] > remain) {
break; // Stop if we exceed the target
}
path.push(candidates[i]);
backtrack(remain - candidates[i], i, path);
path.pop();
}
};
backtrack(target, 0, []);
return result;
};
C# Solution
public class Solution {
public IList> CombinationSum(int[] candidates, int target) {
var result = new List>();
Array.Sort(candidates); // Sort to optimize
Backtrack(candidates, target, 0, new List(), result);
return result;
}
private void Backtrack(int[] candidates, int remain, int start,
List path, IList> result) {
if (remain == 0) {
result.Add(new List(path));
return;
}
for (int i = start; i < candidates.Length; i++) {
if (candidates[i] > remain) {
break; // Stop if we exceed the target
}
path.Add(candidates[i]);
Backtrack(candidates, remain - candidates[i], i, path, result);
path.RemoveAt(path.Count - 1);
}
}
}
Complexity Analysis
- Time Complexity: O(N^(T/M)) where N is the number of candidates, T is the target value, and M is the minimal value among candidates
- Space Complexity: O(T/M) for the recursion stack
Solution Explanation
This solution uses backtracking to find all possible combinations. Here's how it works:
- Sort the candidates array to optimize the solution
- Use backtracking function with parameters:
- remain: remaining sum to achieve
- start: starting index to avoid duplicates
- path: current combination being built
- For each recursive call:
- If remaining sum is 0, we found a valid combination
- Try each candidate from the start index
- Skip if candidate is larger than remaining sum
- Add candidate to path and recurse
- Remove candidate for backtracking
Key points:
- Sorting helps optimize by breaking early
- Same number can be used multiple times
- Start index prevents duplicate combinations
- Backtracking ensures all possibilities are explored