力扣39:组合总和
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
https://blog.csdn.net/qq_42999949/article/details/119713941?spm=1001.2014.3001.5501
做这道题之前,应该先做一下力扣77:组合
思路完全相同,只有一点改变。
本题思路:
首先定义一个结果数组,根据题意,res应是一个二维数组。由于我们递归需要参数startIndex(用来记录下一层递归,搜索的起始位置),所以在内部写一个递归搜索的函数search。
递归函数参数:
递归函数内包含三个参数,一个是startInedx,另一个是path用来存放符合条件结果,path是个一维数组,还有一个是sum,sum变量来统计单一结果path里的总和,其实这个sum也可以不用,用target做相应的减法就可以了,最后如果target==0就说明找到符合的结果了,但为了代码逻辑清晰,我依然用了sum。
递归的终止条件:
终止只有两种情况,sum大于target和sum等于target。sum等于target的时候,需要收集结果。
单层搜索的逻辑:
单层for循环依然是从startIndex开始,搜索candidates集合。
search(path,sum,i);
关键点:不用i+1了,表示可以重复读取当前的数!!
function combinationSum(candidates, target) {
let res=[];
function search(path,sum,startIndex){
if(sum>target) return;
if(sum==target){
res.push(path.slice());
}
for(let i=startIndex;i<candidates.length;i++){
sum+=candidates[i];
path.push(candidates[i]);
search(path,sum,i);// 关键点:不用i+1了,表示可以重复读取当前的数
sum-=candidates[i];// 回溯
path.pop();// 回溯
}
}
search([],0,0);//注意:这里startIndex的初始值是0,因为可以重复读取当前的值
return res;
}
剪枝优化:
对于sum已经大于target的情况,其实是依然进入了下一层递归,只是下一层递归结束判断的时候,会判断sum > target的话就返回。其实如果已经知道下一层的sum会大于target,就没有必要进入下一层递归了。
剪枝:「对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历」。
for (let i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++)
并且需要对candidates数组进行排序
candidates=candidates.sort((a,b)=>a-b);
剪枝后的完整代码:
function combinationSum (candidates, target) {
let res=[];
candidates=candidates.sort((a,b)=>a-b);
function search(path,sum,startIndex){
if(sum>target) return;
if(sum==target){
res.push(path.slice());
}
for (let i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) {
sum+=candidates[i];
path.push(candidates[i]);
search(path,sum,i);
sum-=candidates[i];
path.pop();
}
}
search([],0,0);
return res;
}