494. 目标和(中等)
这是我参与更文挑战的第 3 天,活动详情查看: 更文挑战
题目描述
来源:力扣(LeetCode)
给你一个整数数组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 <= 100
题目解析
构建二叉树
假设有nums数组是{1,2,3},它的所有情况是一共是23有8种,分别是+1+2+3,+1+2-3,+1-2+3,+1-2-3一直到-1-2-3,
我可以构建这样一棵二叉树,从根节点到每个叶子节点的路径就代表其中一种情况,比如第一个叶子节点是+3,从根节点0到叶子节点+3之间所有节点是0,+1,+2,+3,他就代表8种情况之一+1+2+3。这样通过一棵二叉树就表示了所有可能的情况
右边的二叉树表示的是计算后的结果,还是对0,+1,+2,+3这几个节点,根节点是0不变,下一个节点原本是+1,与上一个节点相加0+1是1,再下一个节点原本是+2,与上一个节点相加1+2是3,最后原本是+3,与上一个节点相加3+3是6
所以右边这棵二叉树的叶子节点就是每种情况计算后的值,把每个值跟target比较就可以了
public class Solution {
public int findTargetSumWays(int[] nums, int target) {
int result = 0;
int len = nums.length;
// 用数组表示二叉数,sumLen是二叉树的节点个数
int sumLen = (int) Math.pow(2, len + 1) - 1;
int[] sum = new int[sumLen];
for (int i = 0; i < len; i++) {
// j表示二叉树每行第一个节点的下标
int j = (int) (Math.pow(2, i + 1) - 1);
// k表示二叉树每行最后一个节点的下标
int k = 2 * j;
for (; j <= k; j++) {
// 左子节点等于父节点加上当前元素
if (j % 2 == 1) {
sum[j] = sum[j / 2] + nums[i];
} else {
// 右子节点等于父节点减去当前元素
sum[j] = sum[j / 2 - 1] - nums[i];
}
if(i == len -1 && sum[j] == target){
result++;
}
}
}
return result;
}
}
总结
构建二叉树这个方法并不是很好,相当于暴力法了,而且代码中j,k两个变量的值要找规律才容易看出来
总结一下二叉树的部分性质
- 满二叉树,每层的节点都达到了最大值,上图中就是满二叉树。层数是
n,总节点数是2n-1,第k层上的结点数是2k-1。 - 完全二叉树,除了最后一次,其他每层的节点都达到最大值
- 用一维数组保存二叉树的话,
nums[i]的左孩子是nums[2*i+1],右孩子是nums[2*i+2]。同样的,nums[i]是左节点的话,他的父节点是nums[i/2],nums[i]是右节点的话,他的父节点是nums[i/2-1]