349,组合总和 Ⅳ

想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
在这里插入图片描述

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

示例:

nums = [1, 2, 3] t
arget = 4


所有可能的组合为:

(1, 1, 1, 1)

(1, 1, 2)

(1, 2, 1)

(1, 3)

(2, 1, 1)

(2, 2)

(3, 1)


请注意,顺序不同的序列被视作不同的组合。


因此输出为 7。


答案:

public int combinationSum4(int[] nums, int target) {
    int[] count = new int[1];
    helper(nums, new int[]{target}, count);
    return count[0];
}

private void helper(int[] nums, int[] target, int[] count) {
    if (target[0] == 0) {
        count[0]++;
        return;
    }
    if (target[0] > 0)
        for (int i = 0; i < nums.length; i++) {
            target[0] -= nums[i];
            helper(nums, target, count);
            target[0] += nums[i];
        }
}

解析:

这种是递归加回溯的方式,前面也讲过很多这种类似的题的,也很容易理解,但这种递归效率实在是很差,我们还可以在改进一下,减少重复计算

public int combinationSum4(int[] nums, int target) {
    return helper(nums, target, new HashMap<Integer, Integer>());
}

private int helper(int[] nums, int target, Map<Integer, Integer> map) {
    if (target < 0)
        return 0;
    if (target == 0)
        return 1;
    if (map.containsKey(target))
        return map.get(target);
    int res = 0;
    for (int i = 0; i < nums.length; i++) {
        int cnt = helper(nums, target - nums[i], map);
        if (target >= nums[i])
            map.put(target - nums[i], cnt);
        res += cnt;
    }
    return res;
}

即便是这样,但因为递归的使用,导致运行效率还不是很高,我们可以考虑一下动态规划的使用

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++) {
            if (i - nums[j] >= 0) {
                dp[i] += dp[i - nums[j]];
            }
        }
    }
    return dp[target];
}

这个非常类似于背包问题,但背包问题有数量上的限制,而这道题没有。我们还以上面举的例子,画个图来加深一下理解,我们可以把它当成一棵树,每个节点最多有nums.length个子节点。

在这里插入图片描述

我们看到树中有大量的子树重复,上面代码是按照从树的底往上进行计算的,如果想要从上往下计算我们就要使用递归的方式了,也就是前面两种解法的答案。

在这里插入图片描述

posted @ 2020-09-25 21:50  数据结构和算法  阅读(75)  评论(0编辑  收藏  举报