Day33 力扣动态规划 : 343. 整数拆分 |96.不同的二叉搜索树

Day33 力扣动态规划 : 343. 整数拆分 |96.不同的二叉搜索树

  • 343. 整数拆分
    • 第一印象
    • 看完题解的思路
    • 实现中的困难
    • 代码
  • 96.不同的二叉搜索树
    • 第一印象
    • 看完题解的思路
    • 实现中的苦难
    • 感悟
    • 代码

今天两题都挺有难度,建议大家思考一下没思路,直接看题解,第一次做,硬想很难想出来。

343. 整数拆分

https://programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html
视频讲解:https://www.bilibili.com/video/BV1Mg411q7YJ

第一印象

感觉难在递归公式找不到。不像之前每次只能走1/2步,现在k是在变的了。

看题解吧

哎卧槽看完了,看一遍都没明白。

看完题解的思路

又看了一遍,明白思路了。但是题解里的这一部分:

所以递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});

那么在取最大值的时候,为什么还要比较dp[i]呢?

因为在递推公式推导的过程中,每次计算dp[i],取最大的而已。

我不懂啊,我先去实现一下试试吧

实现的时候就知道为什么了。

比如外层 for 循环来到了数字 5,内层 for 循环就从j = 1开始。

1 * 4 与 1 * dp[4] 比较,拿出最大的max1,给dp [5]

2 * 3 与 2 * dp[3] 比较,拿出最大的max2,给dp [5], 诶这个时候问题就来了,这个max2一定比dp[5] (赋值max1)更大吗???

所以比较的时候要带着dp[5] 一起比较啊

int max = Math.max(j * (i - j), j * dp[i - j]);
dp[i] = Math.max(dp[i], max);

这么写更容易读懂吧我觉得,不会觉得莫名其妙。

其次,因为 14 23 32 41 它是对称的,所以其实 内层for循环 的终止条件到 i/2 就可以了

for (int j = 1; j <= i / 2; j++) {

实现中的困难

悟了!!!

实现没有困难,回去补充一下思路的

代码

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        //初始化
        dp[0] = 0;
        dp[1] = 0;
        dp[2] = 1;
        //递推公式
        for (int i = 3; i < dp.length; i++) {
            for (int j = 1; j <= i / 2; j++) {
                int max = Math.max(j * (i - j), j * dp[i - j]);
                dp[i] = Math.max(dp[i], max);
            }
        }
        return dp[n];
    }
}

96.不同的二叉搜索树

https://programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html
视屏讲解:https://www.bilibili.com/video/BV1eK411o7QA

第一印象

我有思路

Day33 力扣动态规划 : 343. 整数拆分 |96.不同的二叉搜索树_第1张图片

dp数组与初始化:
i 个节点有 dp[i] 种 二叉搜索树,dp[0] 没有意义。初始化 1 个节点,和 2 个节点的情况。

递推公式:
为了数字多一些,直接举 4 的例子。
如果有4个节点,首先节点1 2 3 4可以分别作为根节点。

  • 1 作为根节点的时候,1 和 234,那么根就不用看了,就是234这样的组合有多少种情况,这不就是 3个节点的情况吗,也就是 dp[3]。
  • 2作为根节点,将其分成1和2和34. 左子树 1 其实就是 dp[1] , 右子树 34其实就是dp[2]. 所以有dp[1] * dp[2] 种可能。 要是举例子3个节点的话,就是1和2和3了,左右子树都是 1种情况 可能蒙蔽人,但是我实际在找规律的时候,总要做一下3,当时就差点被蒙蔽了。
  • 3作为根节点,和2的情况是一样的。 也是个对称,所以应该可以优化以下的。
  • 4 作为根节点,和1的情况也是一样的,对称的。

遍历顺序:
从左到右就可以了

我实现一下试试,代码可能不那么容易写。

看完题解的思路

和我想的一样。

但是我的代码一开始写麻烦了,但是逻辑很清晰:

 for (int i = 2; i < dp.length; i++) {
            //模拟一个序列,i个节点
            int[] array = new int[i];
            int count = 0;
            //i个节点的时候,从左到右去选择根节点
            for (int j = 0; j < array.length; j++) {
                //左子树节点数量
                int leftSize = j;
                //右子树节点数量
                int rightSize = i - j - 1;
                // 当前j的二叉搜索树种树
                int curNumber = dp[rightSize] * dp[leftSize];
                count += curNumber;
            }
            dp[i] = count;
            System.out.println(dp[i]);
        }

其实可以直接累加就行了,不需要用数组再去模拟了,回头看,我其实都没用到这个数组……

 for (int i = 2; i < dp.length; i++) {
       //i个节点的时候,从左到右去选择根节点
       for (int j = 0; j < i; j++) {
          //直接用dp数组累加
          dp[i] += dp[j] * dp[i - j - 1];
       }
       System.out.println(dp[i]);
  }

如果优化一下呢,感觉还要考虑奇偶的事情了,哎算了。

实现中的苦难

在统计左右子树节点数量的时候,左子树 j 个,右子树应该是 i - j - 1个。 举例子的时候马虎了

再也没别的什么好说的了

感悟

太牛了,dp天才么这不

代码

class Solution {
    public int numTrees(int n) {
        if (n == 1) return 1;
        // if (n == 2) return 2;

        int[] dp = new int[n + 1];
        dp[0] = 1;
        dp[1] = 1;
        // dp[2] = 2;

        for (int i = 2; i < dp.length; i++) {
            //模拟一个序列,i个节点
            int[] array = new int[i];
            int count = 0;
            //i个节点的时候,从左到右去选择根节点
            for (int j = 0; j < array.length; j++) {
                //左子树节点数量
                int leftSize = j;
                //右子树节点数量
                int rightSize = i - j - 1;
                // 当前j的二叉搜索树种树
                int curNumber = dp[rightSize] * dp[leftSize];
                count += curNumber;
            }
            dp[i] = count;
            System.out.println(dp[i]);
        }

        return dp[n];
    }
}

你可能感兴趣的:(leetcode,动态规划,算法)