CombinationSum组合数之和系列问题

1. leetcode39组合数之和

给定一无重复元素数组和目标数,数组的每个元素可以重复使用找到数组中元素所有可能的组合满足其和等于目标数,要求不能有重复的组合。

  • 经典的组合问题
  • 递归的出口
    • 找到一组解后,自然应当返回上一层
    • 当前的nums[i] > target,也应该直接返回上一层,因为数组排序后的,在这一层再往后找意义不大
  • 每个数字可以重复利用,因此传入下一层的位置不变
  • 但是这并不意味着每一层都是从nums[0]开始,每一层应该从当前位置开始nums[i],这样就可以有效避免由于"往前看"导致的组合数重复
  • 代码
    public static List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> results = new ArrayList<>();
        List<Integer> item = new ArrayList<>();
        if (candidates.length == 0){
            return results;
        }
        Arrays.sort(candidates);
        combinationSumHelper(candidates, target, 0, item, results);
        return results;

    }
    private static void combinationSumHelper(int[] candidates,
                                             int target,
                                             int startIndex,
                                             List<Integer> item,
                                             List<List<Integer>> results){
        if (target == 0){
            results.add( new ArrayList<>(item));
            return;
        }

        for (int i=startIndex; i<candidates.length; i++){
            //target再减就是负数了 又数组升序 继续在这一层遍历下一个 i 已经没有意义了
            // 所以这里直接返回到上一层
            if (candidates[i] > target){
                return;
            }
            item.add(candidates[i]);
            //因为每一个元素可以重复使用 所以这里传入下一层的位置不变还是 i
            // 这里一个很重要的问题在于 每一次递归并不是 从数组的第0位开始的
            // 而是从上一次所在位置开始的
            // 简单的说 就是不准往前看!
            combinationSumHelper02(candidates, target-candidates[i],i,item,results);
            item.remove( item.size() - 1);
        }
    }
  • 配合下图理解会更好
    在这里插入图片描述

2.leetcode40组合数之和2

给定一含重复元素的数组和一目标数,数组的元素只能使用一次求其所有可能的唯一组合,其和等于目标数

  • 含重复元素
  • 数组的元素只能使用一次
  • 所以相比于上题,本地的最大难点就在于去重
  • 这里去重的思路和求子集2的思路完全一致
  • 首先排序
  • 其次对于重复元素,只能从左往右依次添加,不能跳跃
    public static List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<Integer> item = new ArrayList<>();
        List<List<Integer>> results = new ArrayList<>();
        if (candidates.length == 0){
            results.add(item);
            return results;
        }
        Arrays.sort(candidates);
        helper02(candidates, target, 0, item, results);

        return results;
    }
   private static void helper02(int[] candidates,
                               int target,
                               int startIndex,
                               List<Integer> item,
                               List<List<Integer>> results){

        if ( target == 0){
            results.add( new ArrayList<>(item));
            return;
        }
        for (int i=startIndex; i<candidates.length; i++){
            // 规定重复元素只能从左到右依次取 去重思路和子集问题完全一样
            if ( i!=0 && candidates[i] == candidates[i-1] && i!=startIndex){
                continue;
            }

            if (candidates[i] > target){
                return;
            }
            item.add( candidates[i] );
            //题目要求元素 只能用一次 所以i+1
            helper02(candidates, target - candidates[i], i+1, item, results);
            item.remove( item.size() - 1);
        }
    }

3.leetcode216组合数之和3

从数字1-9之间挑选k个数,其和等于n,求所有可能的组合

  • 和上述两题几乎一样
  • 没有重复元素,不需要考虑去重
  • 要求必须为k个数,所以添加到结果集的时候需要多一个判断条件

 public static List<List<Integer>> combinationSum3(int k, int n) {
        int[] nums = {1,2,3,4,5,6,7,8,9};
        List<Integer> item = new ArrayList<>();
        List<List<Integer>> results = new ArrayList<>();
        helper(nums, k, n, 0, item, results);
        return results;

    }
  private static void helper(int[] nums,
                                 int k,
                                 int n,
                                 int startIdnex,
                                 List<Integer> item,
                                 List<List<Integer>> results){
        if ( n == 0 && item.size() == k){
            results.add( new ArrayList<>(item));
            return;
        }

        for (int i = startIdnex; i<nums.length; i++){

            if (item.size() > k){
                continue;
            }
            if (nums[i] > n){
                return;
            }
            item.add(nums[i]);
            helper02( nums, k, n - nums[i],i+1,item, results);
            item.remove( item.size() - 1);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值