前言
组合问题,是继排列问题之后的又一个经典回溯问题,可以说和排列问题一样,有着相同的思路,类似的解法,但是仍然有许多细节需要处理,大部分人都吊死在了组合解二叉树上面,下面,我们一起来探索一下吧!
回溯模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
题目
给你一个 无重复元素 的整数数组candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
示例
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
思路分析
伪代码5
题目意思很明显,找到目标值的元素总和,实际上就是跟排列一样,不断循环candidates
数组并且每次取其中一个元素,最终取得可以得到目标数组的元素数组,压入结果数组,得出结果。
我们也利用一个栈track
来暂时储存我们的目前找到的元素集合。假如让你们来找,你们会怎么做呢?找出所有元素之间的相互组合,然后一一进行比对吗?很显然这确实可以,但是工作量太大,代码效率太低,不建议这么写。所以我们转变一种思路。
我们采用另外一种方式,每次数组的元素进栈的时候,target就减去相应的进栈元素的值,再拿到下一次循环当中去寻找变化之后的target
,这个时候就会出现三种情况:
- 如果
target = 0
,那么就表示已经找到了一组结果; - 如果
target < 0
,那么表示当前的元素不够target值,可以进行下一次循环; - 如果
target > 0
,那么表示当前的元素总和已经超过了target值,所以此时就该进行回退操作,继续上述的判断;
这些都还是大体的思想,还有一些细节,比如当target < 0
的时候,如果到最后的循环的时候,target的值都小于0,那么就表示没有找到目标值,此时我们也进行回退操作,回退到上一次的循环判断;
我们来看看找到一组结果的细节:
candidates = [2,3,6,7], target = 7,
- 循环第一次数组,2进栈,3,6,7待定,
target = 5
- 递归,循环第二次数组,同样的情况,此时track当中已经存在两个2了,3,6,7待定,
target = 3
- 循环第三次数组,2进栈,3,6,7待定,
target = 1
- 循环第四次数组,2此时大于1,这个时候加进2就会使得元素总和超过7,所以此时回退;
- 回退到第三次数组,待定的3进栈,此时
target = 3
,减去之后变为0,压进结果数组,获得结果[2,2,3]
我们根据这种思想找到了一组结果,剩下的结果数组也是这样。就不一一讲解了。
var combinationSum = function(candidates, target) {
let len = candidates.length
let res = []
const findTarget = (candidates,track,target,index)=>{
if(target ===0){
res.push([...track])
return
}
for(let i=index;i<candidates.length;i++){
let cur = candidates[i]
if(cur<target){
track.push(cur)
findTarget(candidates,h,target-cur,i)
track.pop(cur)
}else if(cur>target){
return
}else{
track.push(cur)
res.push([...h])
track.pop(cur)
return
}
}
}
candidates = candidates.sort((a,b)=>a-b) // 从小到大排序
findTarget(candidates,[],target,0)
return res
};
代码分析
我们也是利用回溯目标结果数组,用于返回结果数组,在递归函数当中,当target = 0
时,终止操作,但是在处理数组元素上面,我们需要定义一个cur
变量来表示每次入栈的元素,并将其与变化之后的target
进行比对,然后执行相应的操作,直到target
变为0,找到目标数组;没有找到就返回0即可。
总结
回溯的题目仍旧需要多加练习,总结经验,不断的汲取方法,以后遇到这种题目才不会束手无策!
我是小白,我们一起练习力扣!