LeetCode494. 目标和

在这里插入图片描述
LeetCode494. 目标和

🍕🎉🥓🎊🚗🐷😱❤😀❣🥙🎇🕵️‍♀️😎🎑🎁🎉😚🎇

题目:

  • 给你一个非负整数数组 nums 和一个整数 target 。向数组中的每个整数前添加 '+''-' ,然后串联起所有整数,可以构造一个 表达式
    • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"
  • 返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例:

  • 输入: n u m s = [ 1 , 1 , 1 , 1 , 1 ] , t a r g e t = 3 nums = [1,1,1,1,1], target = 3 nums=[1,1,1,1,1],target=3
  • 输出: 5 5 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. 抽象为01背包: 本题要使表达式结果为 target,既然要为 target,那么就一定有 总和1 - 总和2 = target,这里 总和1'+' 的数的总和,总和2'-' 的数的总和。又 总和1 + 总和2 = sum, sum是固定的,可推出 总和2 = sum - 总和1 。继而可由公式 总和1 - (sum - 总和1) = target 推导出 总和1 = (target + sum) / 2,由于 targetsum 都是固定的,因此 总和1 就能求出来。此时问题就抽象为在集合 nums 中找出和为 总和1 的组合。假设加法的总和为x,那么减法的总和就是 sum - x。我们要求的是 x - (sum - x) = target,即 x = (target + sum) / 2,此时问题就转化为,装满容量为x的背包,有几种方法。这里的 x 即为背包容量大小。
  2. 确定dp数组及下标含义: dp[j] 表示填满容量大小为 j 的背包,有dp[j] 种方法。
  3. 确定递推公式: dp[j] += dp[j - nums[i]]
  4. dp数组初始化: dp[0] = 1
  5. 确定遍历顺序: nums放在外循环,target放在内循环,内循环倒序。

版本一:Java

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0, n = nums.length;
        for(int x: nums) sum += x;
        if(Math.abs(target) > sum) return 0;
        if((target + sum) % 2 == 1) return 0;
        int bagSize = (target + sum) / 2;
        int[] dp = new int[bagSize + 1];
        dp[0] = 1;
        for(int i = 0; i < n; i++)
            for(int j = bagSize; j >= nums[i]; j--)
                dp[j] += dp[j - nums[i]];
        return dp[bagSize];  
    }
}

版本二:C++

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int n = nums.size(), sum = 0;
        for(auto x: nums) sum += x;
        if(abs(target) > sum) return 0;
        if((target + sum) % 2) return 0;
        int bagSize = (target + sum) / 2;
        vector<int> dp(bagSize + 1, 0);
        dp[0] = 1;
        for(int i = 0; i < n; i++)
            for(int j = bagSize; j >= nums[i]; j--)
                dp[j] += dp[j - nums[i]];
        return dp[bagSize];
    }
};

时间复杂度: O ( n × m ) O(n × m) O(n×m),n为正数个数,m为背包容量
空间复杂度: O ( m ) O(m) O(m),m为背包容量

解题思路二:动规(二维数组😀)
在这里插入图片描述

  1. 确定dp数组及下标含义: dp[i][j] 表示前 i 个数,总和为 j 的所有方案的总和。
  2. 确定递推公式: 由第 i 个数为 可以划分为两个集合,分别为 ① nums[i] 取正,此时 dp[i][j] = dp[i - 1][j - nums[i]]j-nums[i]表示总和为 j 是,nums[i]为正,说明第 i 个数选了正,因此总和 j 需要减去第 i 个数。② 同理,nums[i] 取负,则 dp[i][j] = dp[i - 1][j + nums[i]]。而结果是求总方案数,因此dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j + nums[i]]
  3. dp数组初始化: dp[0][0] = 1 表示一个数都不选的时候总和为0,此时只有这一种方案,故初始为1。
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        if(target < -1000 || target > 1000) return 0;  // 题目中 target范围为[-1000, 1000];
        int n = nums.size(), offset = 1000;  // 需要定义一个偏移量,因为状态可能为负,但是数组下标不能为负
        vector<vector<int>> dp(n + 1, vector<int>(2001));
        dp[0][offset] = 1;  // 数组初始化
        for(int i = 1; i <= n; i++)
            for(int j = -1000; j <= 1000; j++){
                if(j - nums[i - 1] >= -1000)  
                    dp[i][j + offset] += dp[i - 1][j - nums[i - 1] + offset];
                if(j + nums[i - 1] <= 1000)
                    dp[i][j + offset] += dp[i - 1][j + nums[i - 1] + offset];
            }
        return dp[n][target + offset];
    }
};
  • 39
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值