给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
示例 1:
输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []
示例 4:
输入: candidates = [1], target = 1
输出: [[1]]
示例 5:
输入: candidates = [1], target = 2
输出: [[1,1]]
1.自己的解法:回溯的思想。一个组合一个组合的判断,当前判断完后,回溯一步,继续判断下一个组合。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> subList = new ArrayList<>();
combinationSum(candidates,target,list,subList,0);
return list;
}
public void combinationSum(int[] candidates,int target,List<List<Integer>> list,List<Integer> subList,int index){
if(target == 0){//目标值为0,说明找到了
list.add(new ArrayList<Integer>(subList));//将此时的队列加入总list中
return;
}else if(target < 0){//目标值小于0,说明当前组合不符合条件,返回
return;
}else{
for(int i = index;i < candidates.length;i++){//从当前下标开始遍历,不能全部从0开始遍历,是为了防止重复组合出现
subList.add(candidates[i]);
combinationSum(candidates,target - candidates[i],list,subList,i);//继续寻找下一个数字
subList.remove(subList.size()-1);//上个组合找完后,回溯,继续寻找下一个i=index+1
}
}
}
}
如果每一次的循环都从0开始,会产生重复的结果:解析来自@liweiwei1419。
2.答案解法:将循环,利用一个下标,改成了递归。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> subList = new ArrayList<>();
combinationSum(candidates,target,list,subList,0);
return list;
}
public void combinationSum(int[] candidates,int target,List<List<Integer>> list,List<Integer> subList,int index){
if(index == candidates.length){//数组内的元素都被遍历完了,直接返回
return;
}
if(target == 0){//如果目标值==0,说明找到了
list.add(new ArrayList<Integer>(subList));//注意要新建一个arrayList加入总list中
return;
}
if(target - candidates[index] >= 0){//如果当前值小于等于目标值,使用当前数字
subList.add(candidates[index]);//加入当前数字
combinationSum(candidates,target - candidates[index],list,subList,index);//查找下一个元素
subList.remove(subList.size() - 1);//找到或者没找到,都要回溯一个元素
}
combinationSum(candidates,target,list,subList,index+1);//跳过当前数字,向后查找
}
}
3.可以进行剪枝提速,这个题目中对数组先进行排序,当target减去一个数小于0后,那么它减去一个更大值仍旧是小于0的,因此小于0后面的那一步部分都可以舍去,不进行。
代码优化为:
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> subList = new ArrayList<>();
Arrays.sort(candidates);//对数组进行排序
combinationSum(candidates,target,list,subList,0);
return list;
}
public void combinationSum(int[] candidates,int target,List<List<Integer>> list,List<Integer> subList,int index){
//小于0的部分已经被剪枝了,因此也不用考虑
if(target == 0){
list.add(new ArrayList<Integer>(subList));
return;
}else{
for(int i = index;i < candidates.length;i++){
if(target - candidates[i] < 0){//剪枝提速
break;
}
subList.add(candidates[i]);
combinationSum(candidates,target - candidates[i],list,subList,i);
subList.remove(subList.size()-1);
}
}
}
}
题源:力扣