「这是我参与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)