LeetCode - 39. 组合总和

208 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。


原题: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 表示满足结果还需要加的数值。

因此,递归的结束条件有两个:

  1. 给定的数组中所有元素都已经遍历玩了,依然没有组成合适的组合,直接退出。
  2. 已经完成了一个符合条件的组合,将 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);
        }
    }
}