题目描述:
给你一个没有重复元素的数组,和一个target
目标值,返回数组中可以使数字和为目标数target
的所有不同组合。什么叫组合?组合就是数组中任意数字组成的集合,不需要连续,组合和顺序无关。这一题中的不同,指的是两个组合中至少一个数字的被选数量不同,例如[2, 3, 3]
和[2, 3, 2]
就是同一个组合,反之则是不同。
思路:
这道题是很典型的回溯。回溯其实就是穷举,最多加点剪枝优化一下。所以这题思想就很简单,把每个元素不断穷举,判断其和是否达到了target
。
步骤:
本题主要是回溯方法怎么写,所以下面步骤是回溯方法的步骤。
1、先定义好回溯方法的入参
这一题入参也很简单,数组,还需要凑齐的和,要从哪个下标开始穷举
2、定义好回溯方法后,方法里首先确定回溯结束的条件
这一题回溯结束条件就是:还需要凑齐的和为0
了,说明该终止本次回溯了
3、定义好终止条件,下面就是开始穷举,伪代码如下
for (int i = startIndex; i < candidates.length; i++) {
将元素放入数组
迭代回溯方法
将元素从数组中删除,回溯
}
解释:
1、本题使用了回溯模版,可以解决很多类似问题,回溯模版在这里总结一下。
void process(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本次递归集合中元素(从开始下标到数组结尾)) {
处理节点;
process(参数); // 递归
回溯,撤销处理结果
}
}
代码:
List<List<Integer>> ans;
List<Integer> list;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
ans = new ArrayList<>();
list = new ArrayList<>();
// 从下标0开始,需要凑齐target的数
process(candidates, target, 0);
return ans;
}
// rest:剩下要凑齐的数字
// startIndex:从哪个下标开始,继续拿值尝试
public void process(int[] candidates, int rest, int startIndex) {
// 剩余要凑的数字为0,说明target已经达到了,放进结果集合中
if (rest == 0) {
ans.add(new ArrayList<>(list));
return;
}
// 从startIndex下标开始取值尝试
for (int i = startIndex; i < candidates.length; i++) {
// 如果当前值 > 剩下要凑齐的数字,那这个值就不用考虑了
if (candidates[i] <= rest) {
// 先将值放进数组
list.add(candidates[i]);
// 去递归找剩下要凑齐的rest - candidates[i]值
// 因为每个数可以无限取,所以下次尝试还是从 i 开始,而不是 i + 1
process(candidates, rest - candidates[i], i);
// 将刚才放进去的值删除,回溯
list.remove(list.size() - 1);
}
}
}