想了解更多数据结构以及算法题,可以关注微信公众号“数据结构和算法”,每天一题为你精彩解答。也可以扫描下面的二维码关注
349,组合总和 Ⅳ_LeetCode

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

示例:

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个子节点。

349,组合总和 Ⅳ_递归_02

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

349,组合总和 Ⅳ_算法_03