1. 题目链接
2. 题目描述
给你一个由 不同 整数组成的数组 nums
,和一个目标整数 target
。请你从 nums
中找出并返回总和为 target
的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
3. 题目示例
示例 1 :
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
示例 2 :
输入:nums = [9], target = 3
输出:0
4. 解题思路
采用动态规划 + 记忆化搜索(自顶向下递归)。核心思想是:
- 对于当前目标值
i
,遍历nums
中的每个数x
,若x <= i
,则组合数等于i - x
的组合数之和。
状态转移方程:
dp[i] = sum(dp[i - x]),其中 x ∈ nums 且 x <= i
关键步骤:
- 递归函数
dfs(i)
计算组成目标值i
的组合数。 - 记忆化数组
memo
缓存已计算的子问题结果。 - 递归终止条件:当
i = 0
时,表示找到一种有效组合。
5. 题解代码
class Solution {
public int combinationSum4(int[] nums, int target) {
int[] memo = new int[target + 1]; // 记忆化数组,记录每个目标值的组合数
Arrays.fill(memo, -1); // 初始标记为未计算状态
return dfs(target, nums, memo); // 从目标值开始递归
}
private int dfs(int i, int[] nums, int[] memo) {
if (i == 0) {
// 递归终止条件:目标值为0时,组合数为1
return 1;
}
if (memo[i] != -1) {
// 已计算过,直接返回缓存结果
return memo[i];
}
int res = 0; // 累计当前目标值的组合数
for (int x : nums) {
// 遍历每个可能的数
if (x <= i) {
// 只有当数不超过当前目标时才有效
res += dfs(i - x, nums, memo); // 递归计算剩余部分的组合数
}
}
return memo[i] = res; // 结果存入缓存并返回
}
}
6. 复杂度分析
时间复杂度:
- 每个目标值
i
(从0
到target
)只会计算一次。 - 每次计算需要遍历数组
nums
(长度为n
)。
总时间复杂度为 O(target × n),其中n
是数组nums
的长度。
空间复杂度:
- 递归调用栈深度为 O(target)(最坏情况下)。
- 记忆化数组占用 O(target) 空间。
综上,空间复杂度为 O(target)。