LeetCode - 39. Combination Sum

来源:互联网 发布:object 数组添加 编辑:程序博客网 时间:2024/05/18 12:34

这道题要求我们找出所有的可行解,一般这种问题我们可以考虑使用backtracking或DFS,通常情况下,我们将回溯法与DFS同等看待,可以用一个等式表示它们的关系:回溯法 = DFS + 剪枝。所以回溯法是DFS的延伸,其目的在于通过剪枝使得在DFS过程中如果满足了回溯条件不必找到叶子结点,就截断这条路径,从而加速DFS。实际上,即使没有剪枝,DFS从下层退回上层的时候也是一个回溯的过程,通常用递归的形式实现比较直观。

但问题是自己水平和能力比较低,即使是在知道要使用backtracking的情况下,依然写不出backtrack函数,通常backtrack函数的参数列表包括:所有解的集合,以便找到可行解的时候将可行解加入其中;当前的解;数据,比如这道题目中的candidates[];目标随着backtracking变化的情况,比如这道题的remain;以及其他辅助的相关变量,比如这道题目中的start。刚刚看到这个解答的时候我有三个地方不是很理解:

1. 为什么在backtrack之前需要对元素进行排序?后来自己试了一下,发现是不用的

2. 为什么要使用list.add(new ArrayList<>(solution))而不是list.add(solution)?

因为solution的类型是ArrayList<Integer>,而它是随着backtracking的过程而不断变化的。如果使用后面这个写法,那么向list中加入的只是一个reference,它指向的内存中元素是在不断变化的,所以要使用前面的写法。进而进一步地推广,在backtracking或者其他问题中,如果想某个容器中添加某个object的时候,最好new一个新的出来加入,而不是直接把当前的object的reference加进来。

3. 为什么在backtrack之后要remove solution中的最后一个元素?

这是在backtacking向上回溯的时候,需要一步一步地删除以前加入的元素,如果没有这一句的话,那么上一条路径的solution也会一直保存在solution中,整个过程如下图所示:


代码如下:

public class Solution {    public List<List<Integer>> combinationSum(int[] candidates, int target) {        List<List<Integer>> list = new ArrayList<List<Integer>>();        // Arrays.sort(candidates);        backtrack(list, new ArrayList<>(), candidates, target, 0);        return list;    }    private void backtrack(List<List<Integer>> list, List<Integer> solution, int[] candidates, int remain, int start){        if(remain < 0) return;        if(remain == 0) {            list.add(new ArrayList<>(solution));            return;        }        // remain > 0        for(int i = start; i < candidates.length; i++){            solution.add(candidates[i]);            backtrack(list, solution, candidates, remain - candidates[i], i);       // not i + 1 because we can reuse same elements            solution.remove(solution.size() - 1);        }    }}


知识点:

1. 上面三个不明白的问题要多想想,都是非常值得注意的细节,要记住

2. 在backtracking或者DFS的时候,最好像上面的图片一样把调用的过程画出来,这样就会清晰很多

0 0
原创粉丝点击