「这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战」
494. 目标和
题目
给你一个整数数组nums
和一个整数target
。
向数组中的每个整数前添加 +
或 -
,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1]
,可以在 2
之前添加 +
,在 1
之前添加 -
,然后串联起来得到表达式 +2-1
。
返回可以通过上述方法构造的、运算结果等于target
的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
提示:
- 1 <= nums.length <= 20
- 0 <= nums[i] <= 1000
- 0 <= sum(nums[i]) <= 1000
- -1000 <= target <= 1000
方法一:回溯
解题思路
设 n = nums.length
,在每个整数前添加 + 或 -,共有 2^n
个表达式
可以使用回溯遍历所有结果,记录符合运算结果等于 target
的数目
代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var findTargetSumWays = function(nums, target) {
const n = nums.length
let count = 0
const backtrack = function(index, sum) {
if (index === n) {
if (sum === target) {
count++
}
} else {
backtrack(index + 1, sum + nums[index])
backtrack(index + 1, sum - nums[index])
}
}
backtrack(0, 0)
return count
};
算法复杂度分析
- 时间复杂度:O(2^n),需要遍历
2^n
种情况,每种表达式计算结果需要O(1)的时间 - 空间复杂度:O(n),空间复杂度主要取决于递归调用的栈空间,栈的深度不超过n
方法二:动态规划
解题思路
设 sum
为所有整数的和,neg
为所有添 - 的整数之和,则有:
sum - neg - neg = target
即:
neg = (sum - target) / 2
故sum - target
应为非负偶数
若上式成立,问题转化为在数组nums
中选取若干元素,使得被选取的元素之和为neg
,计算共有几种选取方案
定义二维数组dp
,dp[i][j]
表示在数组的前i
个数中选取若干元素,和为j
的选取方案数,则最终答案为dp[n][neg]
,n为数组长度
-
i = 0
时,即不选取元素,dp[0][0] = 1
,dp[0][j] = 0 (j != 0)
-
i != 0
时,对于第i
个元素num
若不选该元素,则有
dp[i - 1][j]
种方案,使得和为j
若选取该元素,则有
dp[i][j - num]
种方案,使得和为j
故总方案数:
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - num]
优化
考虑到dp
每一行的计算只与上一行有关,所以不必使用二维数组,可以使用滚动数组,去掉dp
的第一个维度
因为j
较大时的方案数计算依赖j
较小时的方案数,故内存循环应从j
最大值开始倒序遍历,保证转移来的是dp[i - 1][]
的元素
代码
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var findTargetSumWays = function(nums, target) {
const sum = getSum(nums)
const diff = sum - target
if (diff < 0 || diff % 2 !== 0) {
return 0
}
const neg = Math.floor(diff / 2)
const dp = new Array(neg + 1).fill(0)
dp[0] = 1
for (let num of nums) {
for (let j = neg; j >= num; j--) {
dp[j] = dp[j] + dp[j - num]
}
}
return dp[neg]
};
const getSum = function(nums) {
let sum = 0
nums.forEach(item => {
sum += item
})
return sum
}
算法复杂度分析
- 时间复杂度:O(n * (sum - target))
- 空间复杂度:O(sum - target)