# [39] 组合总和
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:
输入:candidates = [2,3,5], target = 8
输出:[[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入:candidates = [2], target = 1
输出:[]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都 互不相同
1 <= target <= 500
这道题用到的是经典的回溯法,解题思路类似于两数之和,记录下离目标值的差值,遍历可用数字加入集合中即可。
难点在于如何让返回结果中的元素集合不重复。
除开建立 hashMap 或者 Set 这样的方式,一般来说,去重的方法基本上都伴随着排序,因为排序能将重复元素聚集在一起,方便剪枝。
我们可以在每次进行子节点递归时,记录当前 index。由于 candidates 已经过排序,因此每一次取值都不会比 path 内中所有元素小,从而避免重复集合的问题。
function combinationSum(candidates: number[], target: number): number[][] {
candidates.sort((a, b) => a - b);
const res: number[][] = [];
backtrack(0, [], target);
return res;
function backtrack(start: number, path: number[], rest: number) {
if (rest === 0) {
res.push([...path]);
}
// 由于已排序,每一次取值都不会比 path 内中所有元素小,从而避免重复组合的问题
for (let i = start; i < candidates.length; i++) {
// 如果剩余值比当前选择小,则跳过
if (rest < candidates[i]) {
break;
}
path.push(candidates[i]);
backtrack(i, path, rest - candidates[i]);
path.pop();
}
}
}