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.
The test cases are generated such that the number of unique combinations that sum up to target
is less than 150
combinations for the given input.
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: []
Solution in Python:
To solve this problem in Python, we will use a backtracking approach. Backtracking is well-suited for exploring all possible combinations and pruning those that do not meet the criteria (i.e., do not sum up to the target).
from typing import List
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
result = []
def backtrack(remain, comb, start):
# If remain is zero, we found a valid combination
if remain == 0:
result.append(list(comb))
return
# If remain is negative, this combination is not valid
elif remain < 0:
return
# Loop through the candidates starting from the current position
for i in range(start, len(candidates)):
# Add the candidate to the current combination
comb.append(candidates[i])
# Since we can use the same element again, we call backtrack with the same start index
backtrack(remain - candidates[i], comb, i)
# Remove the last element to backtrack
comb.pop()
# Sort the candidates to help with pruning (not necessary but can optimize a bit)
candidates.sort()
# Start the backtracking process with an empty combination
backtrack(target, [], 0)
return result
Detailed Explanation:
- Initialization:
- Define a
result
list to store all the valid combinations. - Define a helper function
backtrack
that takes three parameters:remain
: the remaining sum needed to reach the target.comb
: the current combination of numbers.start
: the current index incandidates
from which to consider adding numbers to the combination.
- Define a
- Base Cases:
- If
remain
is zero, a valid combination is found. Append a copy ofcomb
toresult
. - If
remain
is less than zero, the current combination is invalid, so return early.
- If
- Recursion:
- Loop through the
candidates
starting from thestart
index. - Append the current candidate to
comb
. - Recursively call
backtrack
with the updatedremain
(subtract the current candidate fromremain
), the updatedcomb
, and the samestart
index (allowing repeated use of the same candidate). - After the recursive call, remove the last element from
comb
to backtrack and try the next candidate.
- Loop through the
- Sorting:
- Optionally, sort the
candidates
to optimize the process by helping with pruning.
- Optionally, sort the
- Return the Result:
- After all recursive calls complete, return the
result
list containing all unique combinations.
- After all recursive calls complete, return the
This approach ensures that all unique combinations of candidates that sum to the target are found using a depth-first search (DFS) method facilitated by backtracking. The constraints ensure that the problem size is manageable within this approach.
Solution in Javascript:
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum = function(candidates, target) {
const result = [];
/**
* Helper function for backtracking
* @param {number} remain - the remaining sum needed to reach the target
* @param {number[]} comb - the current combination of numbers
* @param {number} start - the current index in candidates from which to consider adding numbers to the combination
*/
const backtrack = (remain, comb, start) => {
// If remain is zero, we found a valid combination
if (remain === 0) {
result.push([...comb]); // Make a copy of comb and add it to result
return;
}
// If remain is negative, this combination is not valid
if (remain < 0) {
return;
}
// Loop through the candidates starting from the current position
for (let i = start; i < candidates.length; i++) {
// Add the candidate to the current combination
comb.push(candidates[i]);
// Since we can use the same element again, we call backtrack with the same start index
backtrack(remain - candidates[i], comb, i);
// Remove the last element to backtrack
comb.pop();
}
};
// Sort the candidates to help with pruning (not necessary but can optimize a bit)
candidates.sort((a, b) => a - b);
// Start the backtracking process with an empty combination
backtrack(target, [], 0);
return result;
};
Solution in Java:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>(); // Store all valid combinations
// Sort the candidates array to help with pruning (not necessary but can optimize a bit)
Arrays.sort(candidates);
// Helper function for backtracking
backtrack(candidates, target, new ArrayList<>(), 0, result);
return result;
}
/**
* Helper function for backtracking
* @param candidates - array of candidate numbers
* @param remain - remaining sum needed to reach the target
* @param comb - current combination of numbers
* @param start - current index in candidates from which to consider adding numbers to the combination
* @param result - list to store all valid combinations
*/
private void backtrack(int[] candidates, int remain, List<Integer> comb, int start, List<List<Integer>> result) {
// Base case: If remain is zero, we found a valid combination
if (remain == 0) {
result.add(new ArrayList<>(comb)); // Make a copy of comb and add it to result
return;
}
// If remain is negative or we have exhausted all candidates, this combination is not valid
if (remain < 0 || start == candidates.length) {
return;
}
// Loop through the candidates starting from the current position
for (int i = start; i < candidates.length; i++) {
// Add the current candidate to the current combination
comb.add(candidates[i]);
// Recursively call backtrack with the updated remain, updated combination, and the same start index
backtrack(candidates, remain - candidates[i], comb, i, result);
// Remove the last element to backtrack
comb.remove(comb.size() - 1);
}
}
}
Solution in C#:
using System;
using System.Collections.Generic;
public class Solution {
public IList<IList<int>> CombinationSum(int[] candidates, int target) {
IList<IList<int>> result = new List<IList<int>>(); // Store all valid combinations
// Sort the candidates array to help with pruning (not necessary but can optimize a bit)
Array.Sort(candidates);
// Helper function for backtracking
Backtrack(candidates, target, new List<int>(), 0, result);
return result;
}
/**
* Helper function for backtracking
* @param candidates - array of candidate numbers
* @param remain - remaining sum needed to reach the target
* @param comb - current combination of numbers
* @param start - current index in candidates from which to consider adding numbers to the combination
* @param result - list to store all valid combinations
*/
private void Backtrack(int[] candidates, int remain, List<int> comb, int start, IList<IList<int>> result) {
// Base case: If remain is zero, we found a valid combination
if (remain == 0) {
result.Add(new List<int>(comb)); // Make a copy of comb and add it to result
return;
}
// If remain is negative or we have exhausted all candidates, this combination is not valid
if (remain < 0 || start == candidates.Length) {
return;
}
// Loop through the candidates starting from the current position
for (int i = start; i < candidates.Length; i++) {
// Add the current candidate to the current combination
comb.Add(candidates[i]);
// Recursively call backtrack with the updated remain, updated combination, and the same start index
Backtrack(candidates, remain - candidates[i], comb, i, result);
// Remove the last element to backtrack
comb.RemoveAt(comb.Count - 1);
}
}
// Helper method to print the result
private static void PrintResult(IList<IList<int>> result) {
foreach (var comb in result) {
Console.Write("[");
Console.Write(string.Join(", ", comb));
Console.WriteLine("]");
}
}
}