题目:
给定一个无重复元素的正整数数组 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 = [1], target = 2
输出: [[1,1]]
分析:
该题和之前的79和80相比大同小异,最主要的不同点在于当选择将数组nums下标为i的数字添加到组合combination中之后,由于nums[i]这个数字可能在组合中重复出现,因此递归调用函数helper时第三个参数传入的指仍然是i,这个参数没有变化,下一步仍然处理数组nums的下标为i的数字。
该题target是组合combination中元素之和的目标值,每当在组合中添加一个数字时,就从target中减去这个数字,当target等于0的时候,组合中的所有元素之和正好等于target,因此也就找到一个符合条件的组合。
当使用回溯法解决问题时尽可能剪枝来优化时间效率,因为题目中明确指出数组中的所有数字都是正整数,因此当组合中已有数字和已经大于目标值的时候(target<0)就没必要考虑数组中还没有处理的数字,因此再在组合中添加任意正整数元素之后和会更大,一定找不到新的符合条件的组合。
代码:
import java.util.LinkedList;
import java.util.List;
public class CombinationSum {
public static void main(String[] args) {
CombinationSum combinationSum = new CombinationSum();
int[] nums = {2,3,5};
List<List<Integer>> lists = combinationSum.combinationSum(nums, 8);
System.out.println(lists);
}
public List<List<Integer>> combinationSum(int[] nums, int target) {
List<List<Integer>> result = new LinkedList<>();
LinkedList<Integer> combination = new LinkedList<>();
helper(nums,target,0,result,combination);
return result;
}
private void helper(int[] nums, int target, int i, List<List<Integer>> result, LinkedList<Integer> combination) {
if (target == 0){
result.add(new LinkedList<>(combination));
//target>0的作用是剪枝,减少不必要的递归,具体分析中有写。
}else if (target >0 && i < nums.length){
helper(nums,target,i+1,result,combination);
combination.add(nums[i]);
// 由于一个数字可以重复在组合中出现,也就是说下一步可能选择同一个数字,因此下一步仍然处理下标为i的数字。
helper(nums,target-nums[i],i,result,combination);
combination.removeLast();
}
}
}