每日一题 -- 树
96. 不同的二叉搜索树
96. 不同的二叉搜索树
分析
- 看到是二叉搜索树,想到了中序遍历递增数组
- 看到是拿 1-n 的序列来求某些合乎规则的值,我想到了 dp,因为每次都是增加一个最大值,那么得到的值总得和前面的状态相关吧。
- 然后我就开始了简单粗暴的找规律做题,然后就挂了
- 所以还是得好好的找函数规律来搞,不要自作聪明
这个才是真的分析
- 这里 dp[n] 其实就是给出 n,然后和会规则的二叉树的数量
- 后面其实忽略一些解题的函数,就是要求出 dp[n], 需要知道一个函数 f(i,n),这个表示以 i 作为节点的时候,有多少二叉树,然后 dp[n] 就是累加的 f(i,n),i = [1,n];
- 然后再将 f(i,n) 转换回以 dp[xxx] 表示的函数,就可以把它省略,最后得到真正的动态规划了
- 在这里 f(i,n) 其实就是以 i 为节点,将数组分成递增的左右两课树,那么这两个树最后得到的值的乘积,其实就是 f(i,n) 的值;即 dp[i-1]*dp[n-i]
- 需要理解的是,左树是 [1,i-1] 好理解,右树是 [i+1,n],但是只要是递增数组转二叉树,其实就之和数组的长度有关,和值是无关的,所有右树最后的值是 dp[n-i]
- 所以最后 dp[n] 就等于累加的 dp[i-1]*dp[n-i]
var numTrees = function (n) {
const dp = new Array(n + 1).fill(1)
for (let x = 1; x < n + 1; x++) {
let temp = 0
for (let i = 1; i < x+1; i++) {
temp += dp[i - 1] * dp[x - i]
}
dp[i] = temp
}
return dp[n]
};
91算法 -- 背包问题
494. 目标和
494. 目标和
分析
- 这里面有坑是,目标值 S 可能是负数,所以用数组的形式去替换 dp 函数了,因为下标是木得负数的
- 同时如果是 0 的话需要特殊处理一下
- 用 map 保存所有值对应的方法
- 最后 map 如果取不到,由于没有预设值,所以需要返回 0
var findTargetSumWays = function (nums, S) {
const total = nums.reduce((pre, cur) => pre + cur, 0)
const dp = Array.from({ length: nums.length })
for (let i = 0; i < nums.length; i++) {
const map= new Map()
for (let c = -total; c <= total; c++) {
if (i === 0) {
if(nums[0] === 0 && c===0){
map.set(0,2)
}else{
map.set(c, Math.abs(c) === nums[0] ? 1 : 0)
}
} else {
const left = c - nums[i] >= -total ? dp[i - 1].get(c - nums[i]) : 0
const right = c + nums[i] <= total ? dp[i - 1].get(c + nums[i]) : 0
const temp =nums[i]===0? dp[i - 1].get(c)*2: left + right
map.set(c, temp)
}
}
dp[i] = map
}
return dp[nums.length-1].get(S) || 0
};