前端就该用 JS 刷算法24

174 阅读2分钟

每日一题 -- 树

96. 不同的二叉搜索树

96. 不同的二叉搜索树

分析

  1. 看到是二叉搜索树,想到了中序遍历递增数组
  2. 看到是拿 1-n 的序列来求某些合乎规则的值,我想到了 dp,因为每次都是增加一个最大值,那么得到的值总得和前面的状态相关吧。
  3. 然后我就开始了简单粗暴的找规律做题,然后就挂了
  4. 所以还是得好好的找函数规律来搞,不要自作聪明

这个才是真的分析

  1. 这里 dp[n] 其实就是给出 n,然后和会规则的二叉树的数量
  2. 后面其实忽略一些解题的函数,就是要求出 dp[n], 需要知道一个函数 f(i,n),这个表示以 i 作为节点的时候,有多少二叉树,然后 dp[n] 就是累加的 f(i,n),i = [1,n];
  3. 然后再将 f(i,n) 转换回以 dp[xxx] 表示的函数,就可以把它省略,最后得到真正的动态规划了
  4. 在这里 f(i,n) 其实就是以 i 为节点,将数组分成递增的左右两课树,那么这两个树最后得到的值的乘积,其实就是 f(i,n) 的值;即 dp[i-1]*dp[n-i]
  5. 需要理解的是,左树是 [1,i-1] 好理解,右树是 [i+1,n],但是只要是递增数组转二叉树,其实就之和数组的长度有关,和值是无关的,所有右树最后的值是 dp[n-i]
  6. 所以最后 dp[n] 就等于累加的 dp[i-1]*dp[n-i]
// https://leetcode-cn.com/problems/unique-binary-search-trees/
// 96. 不同的二叉搜索树
// 题解链接: https://leetcode-cn.com/problems/unique-binary-search-trees/solution/bu-tong-de-er-cha-sou-suo-shu-by-leetcode-solution/

/**
 * 
 * @分析 : 后面可能会被打脸
 * 1. n 递增,相当于每一次都递增一个新的最大值,如果一直 n-1 时的结果值,能够求的 n 时的值吗
 * 2. 看答案。。。
 * 
 * @再分析
 * 1. 答案也确实是用了 dp,但是不是简单的像我拿几个数来猜规律,详细的题解看上面的连接
 * 
 */
var numTrees = function (n) {
    const dp = new Array(n + 1).fill(1)
    for (let x = 1; x < n + 1; x++) {
        //  求 dp[x]的值,需要 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. 目标和

分析

  1. 这里面有坑是,目标值 S 可能是负数,所以用数组的形式去替换 dp 函数了,因为下标是木得负数的
  2. 同时如果是 0 的话需要特殊处理一下
  3. 用 map 保存所有值对应的方法
  4. 最后 map 如果取不到,由于没有预设值,所以需要返回 0
// https://leetcode-cn.com/problems/target-sum/
// 494. 目标和

/**
 * @分析
 * 1. dp[i][c] 表示在下标为 i 的时候,值为 c 的有多少个
 * 
 * @注意
 * 1. 虽然 nums 上都是非负整数,但是 S 并没有说是正数
 */
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
};