LeetCode-组合总和-题号39-Java实现

1、题目链接

组合总和

2、题目大意

给定一个无重复数字的数组 candidates,以及一个目标数字 target,从数组中抽取任意个数字,每个数字可抽取多次,使得抽取的数字之和等于目标数字,问这样的抽取方法有多少种,要求抽取的方法不能重复,比如以下两种便是重复的情况:[1,2,3] 和 [3,1,2]。
注:题中出现的所有数字都是正整数。

3、样例输入

candidates = [2,3,6,7], target = 7
candidates = [2,3,5], target = 8

4、样例输出

[ [7], [2,2,3] ]
[ [2,2,2,2], [2,3,3], [3,5] ]

5、思路

看到这个题,首先想到的就是暴力做法,就是将全部的组合都排列出来,找出组合中等于目标数的组合。然后再看题目中给的数据范围:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500

看这个数据范围,数组的长度最多也才30,暴力的做法是可以试一下的。


刚才说到将全部的数字组合都找出来,这个可以使用回溯法,也就是递归来实现,但是题目中明确说到,每个数字是可以重复使用的,那么全部组合数就是无限了,因此要进行剪枝,最简单的剪枝就是将组合里数字和大于目标数的分支剪掉,因此本题使用回溯法+剪枝


题中还提到一个要求,最后求得的组合不能重复,那么需要在递归函数中加入一个参数,用来记录当前已经使用到了哪一个数字,每次查找一个新的数字组合,都是在以该参数为起始点到最后一个数字的范围内查找,那么就可以避免组合重复了。

6、代码

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        dfs(candidates, target, 0, new ArrayList<>(), ans);
        return ans;
    }
    private void dfs(int[] candidates, int target, int index, List<Integer> list, List<List<Integer>> ans) {
        if (target == 0) {
            ans.add(list);
            return;
        }
        for (int i = index; i < candidates.length; i++) {
            if(target>=candidates[i]) {
                list.add(candidates[i]);
                dfs(candidates, target-candidates[i], i, new ArrayList<>(list), ans);
                list.remove(list.size() - 1);
            }
        }
    }
}

7、注意事项

代码中有个点需要特别注意,大家看下面这句代码:

dfs(candidates, target-candidates[i], i, new ArrayList<>(list), ans);

可以看到传参中,我在传 list 的时候,新建了一个列表,这是因为如果我直接将list传进去,那么在递归栈中的所有 list 都是同一个对象,那么在 ans 列表中存的 list 也都是同一个对象,可以将上述代码改为下列代码做一个测试:

dfs(candidates, target-candidates[i], i, list, ans);

可以发现,无论怎样,最后的 ans 输出的值都是空的。这是因为存在 ans 里的列表都是最初传进来的 list,我们在递归的时候,做的操作是先往 list 里丢一个数,然后递归到下一层,等递归完后,再把丢进去的书移除,那么到最后,其实 list 还是刚开始的样子,是空,而 ans 里存的就是这个为空的 list。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值