题目:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
自己做题的思路:
可能上来一下没有思路,但是我们可以先手动模拟下求示例的解
第一步包含2的解
8 = 2+6 = 2+2+4 = 2+2+2+2;
8=2+3+3;
此时以2为开始的结束了;
第二步以3为开头
8 = 3+5 = 3+2;
第三步一5开头:
8 =5+3;
好,列出了所有的情况,不难看出这是一个多路树的结构,即使没看出来也没关系
反正我们应该能明白思路:将目标target一步步从集合相减一个数n,剩下的值target-n继续进行相同的操作(递归);
但是递归也要有个终止条件:target变成了0,小于0的情况,如果target变成则从头到尾的调用链就是解,如果小于0就抛弃
这里说下回溯和递归的区别:递归是一种算法结构,回溯是一种算法思想,可以用递归实现。通俗点讲回溯就是一种试探, 类似于穷举,但回溯有“剪枝”功能:比如本题中的
来优化算法效率。
另外会发现现在求出的解有重复,怎么去重呢?方案很多这里使用的是先排序,然后递归过程中遍历的newCandidates只遍历大于之前的减数就可以了
上代码:
public class Solution {
public static void main(String args[]) {
int[] candidates = new int[]{2, 3, 5};
int target = 8;
List<List<Integer>> res = new ArrayList<>();
//第一步排序
Arrays.sort(candidates);
//去掉>target的数据
List<Integer> newCandidates = new ArrayList<>();
for (int i : candidates) {
if (i <= target) {
newCandidates.add(i);
}
}
//递归搜索
find(res, new ArrayList<>(), target, newCandidates, 0);
//输出结果
for (List<Integer> i : res) {
for (int k : i) {
System.out.print(k);
}
System.out.println("-");
}
}
public static void find(List<List<Integer>> resList, List<Integer> res, int target, List<Integer> newCandidates, int start) {
//如果减去的target=0就是结果,需要把数组放入结果集resList
if (target == 0) {
resList.add(new ArrayList<>(res));
return;
} else if (target < 0) {
return;
}
//target>0继续递归
for (int i = start; i < newCandidates.size(); i++) {
int newTarget = target - newCandidates.get(i);
//小于0就返回了,因为后面的被减数都比newCandidates.get(i)大没必要继续了
if(newTarget<0){
return ;
}
//如果>0就把当前被减数加入res中
res.add(newCandidates.get(i));
find(resList, res, newTarget, newCandidates, i);
//删除掉刚才加入res的节点,因为要进行下一步的newCandidates.get(i+1)的判断,要去掉刚才加入的
res.remove(res.size() - 1);
}
}
}