开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天,点击查看活动详情
题目:LeetCode
给定一个候选人编号的集合 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 <= 1001 <= candidates[i] <= 501 <= target <= 30
解题思路
由题意分析:利用回溯的方法大致思路是:
- 遍历数组中的值,如果nums[i] < target , 尝试把nums[i]作为一个加数,把目标值减去nums[i],下一次递归从i+1开始遍历数组寻找下一个加数
- 如果target=0,说明找到了一组加数
- 否则把上一个加数从list中去掉
去重:需要先对数组进行排序,每次递归寻找的是一个位置的加数值,同一个位置不能使用相同的加数值
明确变化的状态
- 从左到右开始选,每选一个数,我们都可以使用target - candidates[i]表示接下来还有凑出来的目标数
- 选完后,下一个可以选择的数从i + 1开始
- 保留选过的数。
代码实现
List < List < Integer >> ans = new ArrayList < > ();
public List < List < Integer >> combinationSum2(int[] candidates, int target) {
if (candidates.length == 0)
return ans;
Arrays.sort(candidates);
helper(candidates, 0, target, new ArrayList < > ());
return ans;
}
private void helper(int[] nums, int l, int target, List < Integer > list) {
if (target == 0) {
ans.add(new ArrayList < > (list)); //存入的是复制的list对象,避免递归时修改list也修改了ans中的解
//list = new ArrayList<>();
return;
}
for (int i = l; i < nums.length; i++) {
//[10,1,2,7,6,1,5] => [1,1,2,5,6,7,10] target=8
//假设现在寻找第一个加数,nums[0] = 1 ,第一个加数是位置0的1,然后可以找到 [1,1,6] [1,7],最后递归回归到i=0,即寻找第一个加数的递归函数
//尝试nums[1]作为第一个加数, 此时发现nums[1] = nums[0] ,那么递归下去一定可以找到重复的解[1,7], 因此跳过nums[1]
// i > l的目的是 确保不是第一次使用这个加数,如果不保证i > l, 那么[1,1,6]这样的解就会丢失
if (i > l && nums[i] == nums[i - 1])
continue;
if (target - nums[i] >= 0) {
list.add(nums[i]);
helper(nums, i + 1, target - nums[i], list);
if (list.size() > 0)
list.remove(list.size() - 1); //没有找到一组解
}
}
}
运行结果
复杂度分析
- 空间复杂度:
- 时间复杂度:
在掘金(JUEJIN) 一起分享知识, Keep Learning!