leetcode 494题——目标和

257 阅读1分钟

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

算法复杂度分析

  1. 时间复杂度:O(2^n),需要遍历 2^n 种情况,每种表达式计算结果需要O(1)的时间
  2. 空间复杂度:O(n),空间复杂度主要取决于递归调用的栈空间,栈的深度不超过n

方法二:动态规划

解题思路

sum 为所有整数的和,neg 为所有添 - 的整数之和,则有:

sum - neg - neg = target

即:

neg = (sum - target) / 2

sum - target应为非负偶数

若上式成立,问题转化为在数组nums中选取若干元素,使得被选取的元素之和为neg,计算共有几种选取方案

定义二维数组dpdp[i][j]表示在数组的前i个数中选取若干元素,和为j的选取方案数,则最终答案为dp[n][neg],n为数组长度

  1. i = 0 时,即不选取元素,dp[0][0] = 1dp[0][j] = 0 (j != 0)

  2. 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
}

算法复杂度分析

  1. 时间复杂度:O(n * (sum - target))
  2. 空间复杂度:O(sum - target)