LeetCode - 39. Combination Sum

这道题要求我们找出所有的可行解,一般这种问题我们可以考虑使用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的时候,最好像上面的图片一样把调用的过程画出来,这样就会清晰很多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值