这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
前言
力扣第三十九题 组合总和 如下所示:
给定一个无重复元素的正整数数组 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]]
提示:
candidate 中的每个元素都是独一无二的
一、思路
这一题思路很简单,就是在数组中找到所有和为 target 的组合(数值中的数字可重复选取)
题目中有两个比较重要的信息:
- 数组中无重复元素
- 数组中的值可重复选取
根据上面两个信息所以在递归时需要注意以下两点:
- 因为数组中无重复元素,所以在递归时无需考虑结果集中是否有重复的结果
- 因为数组中的值可重复选取,所以递归的起点应跟循环中的起点一致。(假设结果中的第一个元素选择以下标
1作为起点,则第二个元素也需要以下标1作为起点)
举个例子
此处以示例1中的 candidates = [2,3,6,7], target = 7 作为例子
tips:遍历按照从左至右的顺序,使用
栈来存储遍历的路径
- 选取第一个元素
2,此时2 < 7,元素2入栈 - 递归1:继续选取第一个元素
2,此时(2 + 2) < 7,元素2入栈 - 递归2:继续选取第一个元素
2,此时(2 + 2 + 2) < 7,元素2入栈。此时栈为2 -> 2 -> 2 - 递归3:继续选取第一个元素
2,此时(2 + 2 + 2 + 2) > 7,无需入栈,且无需向下递归 - 递归3:继续选取第二个及之后的元素。很显然没有满足的结果,故栈顶出栈。此时栈为
2 -> 2,此层递归结束 - 递归2:继续选择第二个元素
3,此时(2 + 2 + 3) == 7,此时找到了第一个正确的结果[2, 2, 3] - ......,其他的结果递归的过程与第一个结果类似
- 最终可以得到正确的结果:
[2, 2, 3], [7]
二、实现
实现代码
实现代码与思路中有一点不一样:
为了避免遍历栈来获取总和,所以直接将下一次递归的 target 置为 target - candidates[i],如果 target == 0 则代表找到了一个正确的结果
/**
* 回溯算法
*/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> ret = new ArrayList<>();
int len = candidates.length;
if (len == 0)
return ret;
Stack<Integer> path = new Stack<>();
dfs(candidates, 0, target, path, ret);
return ret;
}
/**
* 回溯的实现
* @param candidates 候选数组
* @param begin 开始搜索位置
* @param target 目标值
* @param path 路径
* @param ret 结果集
*/
private void dfs(int[] candidates, int begin, int target, Stack<Integer> path, List<List<Integer>> ret) {
// 剪枝:target为负值和0时不分支
if (target < 0)
return;
if (target == 0) {
ret.add(new ArrayList<>(path));
}
// 从begin开始搜索
for (int i=begin; i<candidates.length; i++) {
// 路径进栈
path.push(candidates[i]);
// 因为会重复选取,所以下次搜索起点为i
dfs(candidates, i, target - candidates[i], path, ret);
// 路径出栈
path.pop();
}
}
测试代码
public static void main(String[] args) {
int[] nums = {2,3,6, 7};
new test().combinationSum(nums, 7);
}
结果
三、总结
虽然这一题是写出来了,但是击败率不是很理想。
看了一下别人写的算法,采用的是 先排序 + 再剪枝,可以有效的减少递归的次数。(非常不错的思路,点赞~)
感谢看到最后,非常荣幸能够帮助到你~♥