给你一个整数数组
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示例 2:
输入:nums = [1], target = 1 输出:1提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
思路:
dfs,注意判断边界条件
补充:力扣上的背包问题:
- 01背包:416. 分割等和子集 474. 一和零 494. 目标和 879. 盈利计划 1049. 最后一块石头的重量 II 1230. 抛掷硬币
- 完全背包:1449. 数位成本和为目标值的最大数字 322. 零钱兑换 518. 零钱兑换 II 279. 完全平方数
时间复杂度:
O(2^n),其中 n 是数组 nums 的长度。回溯需要遍历所有不同的表达式,共有 2^n 种不同的表达式,每种表达式计算结果需要 O(1) 的时间,因此总时间复杂度是 O(2^n)。
空间复杂度:
O(n),其中 n 是数组 nums 的长度。空间复杂度主要取决于递归调用的栈空间,栈的深度不超过 n。
// 方式1 dfs:不断更新sum值,并与target作比较
var res int
func findTargetSumWays(nums []int, target int) int {
res = 0 // 要加初始化条件,避免多个测试用例导致结果覆盖
dfs(nums, target, 0, 0)
return res
}
func dfs(nums []int, target, sum, k int) { // k表示当前遍历到的nums下标
if k == len(nums) {
if sum == target {
res++
}
return
}
// 这两个判断不能相反,因为他们存在共同条件 k == len(nums)
// if sum == target && k == len(nums) { // 注意:这里不是 k == len(nums) - 1,因为nums最后一个元素也要累加上
// res++
// return
// }
// if k >= len(nums) /*|| sum > target*/ { // sum > target:不加这个条件,有可能后面会累加一个负数的情况出现
// return
// }
dfs(nums, target, sum + nums[k], k+1) // 加一个数
dfs(nums, target, sum - nums[k], k+1) // 减一个数
return
}
/**********************************************************************/
// 方式2:不断更新target值
var res, n int
func findTargetSumWays(nums []int, target int) int {
res, n = 0, len(nums)
dfs(nums, target, 0)
return res
}
func dfs(nums []int, target, i int) {
if i == n {
if target == 0 {
res++
}
return
}
dfs(nums, target - nums[i], i + 1)
dfs(nums, target + nums[i], i + 1)
}