Leetcode: Combination Sum

已知数组C和目标值T,要求在C中找出所有独特的组合使其和为T,C中所有的数和T都为正整数

39. Combination Sum

Note:
1. C中无重复
2. 同一个数在组合中可以出现任意次

比如C=[2, 3, 6, 7] ,T=7, 解为

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

解法

简单回溯

public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        findCombination(result, new ArrayList<Integer>(), candidates,  target, 0);
        return result;
    }

    public void findCombination(List<List<Integer>> result, List<Integer> combinations, int[] candidates, int remain, int start){
        if(remain < 0) return;
        if (remain == 0){
            result.add(new ArrayList<Integer>(combinations));
        }
        //每递归一层for循环确定一个位置的数,如第k层for循环就确定第k个数
        for(int i = start; i < candidates.length; i++) {
            combinations.add(candidates[i]);
            findCombination(result, combinations, candidates, remain - candidates[i], i);//start置为i表明允许多次使用同一个数
            combinations.remove(combinations.size()-1);
        }
    }
}

简单回溯 + 排序优化

public class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        //排序优化
        Arrays.sort(candidates);
        findCombination(result, new ArrayList<Integer>(), candidates,  target, 0);
        return result;
    }

    public void findCombination(List<List<Integer>> result, List<Integer> combinations, int[] candidates, int remain, int start){
        if(remain < 0) return;
        if (remain == 0){
            result.add(new ArrayList<Integer>(combinations));
        }
        //每递归一层for循环确定一个位置的数,如第k层for循环就确定第k个数
        for(int i = start; i < candidates.length && candidates[i] <= remain; i++) { //当c[i]大于remain,则i以及i以后的都不会符合
            combinations.add(candidates[i]);
            findCombination(result, combinations, candidates, remain - candidates[i], i);
            combinations.remove(combinations.size()-1);
        }
    }
}

40. Combination Sum II

Note:
1. C中可能有重复值
2. C中的每一个数在组合中只能出现一次

如C = [10, 1, 2, 7, 6, 1, 5], T = 8, 解为

[
  [1, 7],
  [1, 2, 5],
  [2, 6],
  [1, 1, 6]
]

解法

利用排序去重

public class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(candidates);//先排序以便去重
        findCombination(result, new ArrayList<Integer>(), candidates,  target, 0);
        return result;
    }

    public void findCombination(List<List<Integer>> result, List<Integer> combinations, int[] candidates, int remain, int start){
        if(remain < 0) return;
        if (remain == 0){
            result.add(new ArrayList<Integer>(combinations));
        }
        //每层for循环确定一个位置的数,如第k层确定第k个数
        for(int i = start; i < candidates.length && candidates[i] <= remain; i++) {
            //保证当前位置不会重复使用相等的数,如1,1,2,5,6,7,10,位置0只可能使用第一个1,位置1只可能使用第二个1
            if(i > start && candidates[i] == candidates[i-1]) continue;
            combinations.add(candidates[i]);
            findCombination(result, combinations, candidates, remain - candidates[i], i + 1);
            combinations.remove(combinations.size()-1); 
        }
    }
}

377. Combination Sum IV

只需求出所有可能组合的数量

Note:
1. C中无重复
2. C中的每一个数在组合中可出现任意次

如C = [1, 2, 3], T = 4, 则

可能的所有组合为
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)

故结果为7.

解法

递推公式:comb[target] = sum(comb[target - nums[i]]) (0 <= i < nums.length && target >= nums[i]),comb[0] = 1。

递归,会超时,因为存在的大量重复计算(调用)

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        if (target == 0) return 1;
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            if (target >= nums[i]) {
                res += combinationSum4(nums, target - nums[i]);
            }
        }
        return res;
    }
}

动态规划(从dp[0]到dp[target]都要计算,其中大部分可能都是不必要的)

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        int [] dp = new int[target + 1];
        dp[0] = 1;
        for(int i = 1; i <= target; i++){
            for(int j = 0; j < nums.length; j++){
                dp[i] += (i - nums[j]) < 0 ? 0 : dp[i-nums[j]];
            }
        }
        return dp[target];
    }
}

动态规划+递归(可以将dp[]看作缓存),避免了重复计算和不必要的计算

public class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        Arrays.fill(dp, -1);
        dp[0] = 1;
        return helper(nums, target, dp);
    }

    private int helper(int[] nums, int target, int[] dp) {
        //已计算过直接返回
        if (dp[target] != -1)   return dp[target];

        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            if (target >= nums[i]) {
                res += helper(nums, target - nums[i], dp);
            }
        }
        dp[target] = res;
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值