小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原题:39. 组合总和
给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。
candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。
解题思路:
因为要从给定的数组中找到不同的排列组合,最直观的办法就是使用 DFS 算法,找到所有的排列组合,然后把符合要求的组合筛选出来。但是,题目中说,每一个元素可以使用多次,因此,可能的排列组合是无穷多的,所以,要在 DFS 的递归调用中,格外注意递归的推出条件。
先写出大的框架:
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> seq = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
dfs(0, candidates, target);
return result;
}
private void dfs(int start, int[] candidates, int target) {
}
}
其中的两个变量
result是最终的结果集seq是一个符合条件的排列组合,每当排列出一个完整的组合,就放入result当中
在 dfs 方法中,start 参数代表从某个下标开始循环数组中的所有元素,组合到一个结果中;每当向组合中添加一个元素,就从 target 中减去,因此,target 表示满足结果还需要加的数值。
因此,递归的结束条件有两个:
- 给定的数组中所有元素都已经遍历玩了,依然没有组成合适的组合,直接退出。
- 已经完成了一个符合条件的组合,将
seq加入到结果集result中后退出。
对应代码就是:
if (target == 0) {
result.add(new ArrayList<>(seq));
return;
}
if (start == candidates.length) {
return;
}
最后再来分析递归中的逻辑代码。当未达到退出条件的时候,就需要继续向 seq 中添加元素,从 start 下标开始,遍历数组中的所有元素,添加到 seq 中。
这里需要一个判断,如果当前的元素比 target 大,那么就不能被加入到 seq 中,因为数组中的元素都是正整数,如果加入一个元素后导致组合的总和超过题目给定的目标值,那么就不能成为符合条件的组合。
然后在递归执行方法,最后在删掉 seq 中的元素恢复状态。
递归执行方法的时候,传入的 start 参数为循环到的下标 i,也就是本层调用的 start 也在其中,因为题目允许一个元素多次使用,传入的 target 则要减去当前层加入组合的元素的值。
for (int i = start; i < candidates.length; i++) {
if (candidates[i] > target) {
continue;
}
seq.add(candidates[i]);
dfs(i, candidates, target - candidates[i]);
seq.remove(seq.size() - 1);
}
最终代码:
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> seq = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
dfs(0, candidates, target);
return result;
}
private void dfs(int start, int[] candidates, int target) {
if (target == 0) {
result.add(new ArrayList<>(seq));
return;
}
if (start == candidates.length) {
return;
}
for (int i = start; i < candidates.length; i++) {
if (candidates[i] > target) {
continue;
}
seq.add(candidates[i]);
dfs(i, candidates, target - candidates[i]);
seq.remove(seq.size() - 1);
}
}
}