问题描述
力扣https://leetcode.cn/problems/Ygoe9J/
给定一个无重复元素的正整数数组 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 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500
解题思路
此题是回溯算法中无重复但每个数字可以多次选择的题目,如果对每个数字都进行无选择的递归的话,结果一定是栈溢出的,所以我们要选择合适的剪枝策略来优化递归过程,我们选择的优化策略如下:如果当前下标的数组的值大于target,直接将这种结果剪掉,如果小于等于的话,进行下一步的递归,并同时更新target,如果最终target==0,直接将此种结果加入到最终结果中,这层递归结束,如果index==数组长度,递归结束
实例代码
class Solution {
private LinkedList<List<Integer>>res=new LinkedList<>();
private LinkedList<Integer>data=new LinkedList<>();
private int sum=0;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
reverse(candidates,target,0);
return res;
}
public void reverse(int []candidates,int target,int index){
//递归出口
if(index==candidates.length){
return;
}
if(target==0){
res.add(new LinkedList(data));
return;
}
//不选择当前数字
reverse(candidates,target,index+1);
//选择当前数字
if(target-candidates[index]>=0){
data.add(candidates[index]);
reverse(candidates,target-candidates[index],index);
//回溯
data.removeLast();
}
}
}
问题描述
力扣https://leetcode.cn/problems/4sjJUc/
给定一个可能有重复数字的整数数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次,解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]
提示:
1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30
解题思路
此题与上文中的题目的条件恰好相反,此题是选择有重复数字,但是每个数字只能选择一次,在这种条件下, 我们必须将相同的数字进行剪枝,必然需要涉及到使重复的数字相邻,(将数组进行排序即可),那么回溯的过程中我们又该如何去除相邻相同数字的组合呢?在递归函数中加入循环即可(需要思想的转化,在递归函数中加入循环,从index(当前指标)的位置直到数组的最后,递归到的每个数组位置都需要加入数据集中,与我们单纯使用下标进行递归的过程是完全一致的),所以当数组相邻元素相等时(nums[i]==nums[i-1]),在直接跳过这个数字即可(因为上一层循环已经用过该数字了),这样进行递归和回溯,直到target==0或者走到数组最后一个元素后方
实例代码
class Solution {
private LinkedList<List<Integer>>res=new LinkedList<>();
private LinkedList<Integer>data=new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
reverse(candidates,0,target);
return res;
}
public void reverse(int[]candidates,int index,int target){
if(target==0){
res.add(new LinkedList(data));
return;
}
for(int i=index;i<candidates.length;++i){
if(candidates[i]>target){
break;
}
if(i>index&&candidates[i]==candidates[i-1]){
continue;
}
data.add(candidates[i]);
reverse(candidates,i+1,target-candidates[i]);
data.removeLast();
}
}
}