这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战
今日题目
给定一个数组 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
思路
这道题相较于组合总和1的区别是:
- 1、每个元素不能再无限使用,而是只能用一次,因此每次递归的下标不能再从当前小标开始,而是i+1开始。
- 2、元素有重复元素,因此需要去重。
上述两点都有去重的效果,一个是在结果上保证了没有重复元素,一个是保证在横向遍历的时候保证没有重复搜索。
除此之外与大多数回溯思路一样。
递归遍历数组,每次首先判断当前下标元素是否与前一个元素相同,如果相同同时判断上一个元素是否正在使用,如果没有使用则直接跳过,因为上一个元素前面已经遍历过了。
然后判断当前元素是否小于等于target,如果大于target,则说明如果target减去当前元素差一定小于0,不满足等于0,直接break循环就可以了。因为该元素之后的元素一定是大于等于当前元素的,因此target减去该元素的差也一定是小于0的,所以直接跳过。这是一步非常重要的剪枝操作。
将当前元素加入path列表,将当前vis[i]设置为true表示已访问。
进入递归,判断target是否为0,如果为0,加入答案列表。
出递归,需要进行回溯,将当前元素从path列表中删除,并将访问置为false。
总结成两个问题:
- 1.如何保证每个数字在每个组合中只使用一次?
答:每次递归调用方法时,从当前数的下一个数开始 - 2.如何保证解集不能包含重复的组合?
答:在有序数组中,如果索引i处的数字 等于 索引i-1处的数字,就直接跳过i,进入下一轮循环
还是使用如下框架代码:
1 | java复制代码result = [] |
代码实现:
1 | java复制代码List<List<Integer>> result = new LinkedList<>(); |
执行用时:2 ms, 在所有 Java 提交中击败了99.32%的用户
内存消耗:38.4 MB, 在所有 Java 提交中击败了85.81%的用户
小结
回溯算法解题整体都是有着类似的目的,都是在寻找所有可行解的题,我们都可以尝试用搜索回溯的方法来解决。同时要记住算法实现的基本框架,可以大大减少实现难度。
本文转载自: 掘金