题目描述:
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
示例:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
题解:
思路与算法:
对于这类寻找所有可行解的题,我们都可以尝试用「搜索回溯」的方法来解决。
回到本题,我们定义递归函数 dfs(target, combine, idx) 表示当前在 candidates 数组的第 idx 位,还剩 target 要组合,已经组合的列表为 combine。递归的终止条件为 target <= 0 或者 candidates 数组被全部用完。那么在当前的函数中,每次我们可以选择跳过不用第 idx 个数,即执行 dfs(target, combine, idx + 1)。也可以选择使用第 idx 个数,即执行 dfs(target - candidates[idx], combine, idx),注意到每个数字可以被无限制重复选取,因此搜索的下标仍为 idx。
更形象化地说,如果我们将整个搜索过程用一个树来表达,即如下图呈现,每次的搜索都会延伸出两个分叉,直到递归的终止条件,这样我们就能不重复且不遗漏地找到所有可行解:
代码:
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> ele = new ArrayList<>();
calculate(candidates, target, result, ele, 0);
return result;
}
public void calculate(int[] candidates, int target, List<List<Integer>> result, List<Integer> ele, int index){
if(index == candidates.length ){
return;
}
if(target == 0){
result.add(new ArrayList(ele));
return;
}
// 跳过当前元素, 取下一元素
calculate(candidates, target, result, ele, index+1);
// 计算当前元素
if(target >= candidates[index]){
ele.add(candidates[index]);
calculate(candidates, target-candidates[index], result, ele, index);
ele.remove(ele.size()-1); // 如果最终没有不满足条件, 则逐步移除元素
}
}
待优化: 搜索回溯的过程一定存在一些优秀的剪枝方法来使得程序运行得更快,而这里只给出了最朴素不含剪枝的写法,因此欢迎各位读者在评论区分享自己的见解。