代码随想录算法训练营第41天| 343. 整数拆分 96.不同的二叉搜索树

JAVA代码编写

343. 整数拆分

  • 给定一个正整数 n ,将其拆分为 k正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

    返回 你可以获得的最大乘积

    示例 1:

    输入: n = 2
    输出: 1
    解释: 2 = 1 + 1, 1 × 1 = 1。
    

    示例 2:

    输入: n = 10
    输出: 36
    解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
    

    提示:

    • 2 <= n <= 58

教程:https://programmercarl.com/0343.%E6%95%B4%E6%95%B0%E6%8B%86%E5%88%86.html

方法一:动态规划

思路:能用回溯做,但是不会

  1. 步骤
    1. 定义$dp[i] $数组:表示数字i拆分后的最大乘积
  2. 递推公式dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
    • dp[i] :这里需要比较dp[i]是因为n比较大的时候,内循环会多次遍历,原本还在想dp[i]取最大了干嘛
    • max((i - j) * j, dp[i - j] * j)
      • (i - j) * j:不太理解,代码中j是指k个和中的一个,通过j <= i-j控制j,这样这里就比较好理解,这里是局部的积,算的dp[i],这里的i可能是小于n的
      • dp[i - j] * j:这个很好看懂,i-j+j=i,就是算的dp[i]
  3. dp数组初始化: dp[2] =1
  4. 确定遍历顺序:根据递推公式,从前往后
  5. 举例推导dp数组,以n=6举例
    代码随想录算法训练营第41天| 343. 整数拆分 96.不同的二叉搜索树_第1张图片

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度:O(n)
class Solution {
    public int integerBreak(int n) {
        //dp[i] 为正整数 i 拆分后的结果的最大乘积
        int[] dp = new int[n+1];
        dp[2] = 1;
        for(int i = 3; i <= n; i++) {
            for(int j = 1; j <= i-j; j++) {
                // 这里的 j 其实最大值为 i-j,再大只不过是重复而已,
                //并且,在本题中,我们分析 dp[0], dp[1]都是无意义的,
                //j 最大到 i-j,就不会用到 dp[0]与dp[1]
                dp[i] = Math.max(dp[i], Math.max(j*(i-j), j*dp[i-j]));
                // j * (i - j) 是单纯的把整数 i 拆分为两个数 也就是 i,i-j ,再相乘
                //而j * dp[i - j]是将 i 拆分成两个以及两个以上的个数,再相乘。
            }
        }
        return dp[n];
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        System.out.println(solution.integerBreak(6));
    }
}

方法二:回溯

思路

复杂度分析

  • 时间复杂度: O ( 2 n ) O(2^n) O(2n)
  • 空间复杂度:O(n)
class Solution {
    private int maxProduct = 0;

    public int integerBreak(int n) {
        if (n == 2) {
            return 1;
        }
        if (n == 3){
            return 2;
        }
        backtrack(n, 1, 1);
        return maxProduct;
    }

    private void backtrack(int n, int start, int product) {
        if (n == 0) {
            maxProduct = Math.max(maxProduct, product);
            return;
        }

        for (int i = start; i <= n; i++) {
            backtrack(n - i, i, product * i);
        }
    }

}

96.不同的二叉搜索树

  • 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

    示例 1:

    代码随想录算法训练营第41天| 343. 整数拆分 96.不同的二叉搜索树_第2张图片

    输入:n = 3
    输出:5
    

    示例 2:

    输入:n = 1
    输出:1
    

    提示:

    • 1 <= n <= 19

教程: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

方法一:动态规划

思路

步骤

  1. 定义$dp[n] $数组:由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有dp[n]种
  2. 递推公式:dp[i] += dp[j - 1] * dp[i - j];j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量
  3. dp数组初始化:dp[0] =1.空节点也是一棵二叉树,也是一棵二叉搜索树,这是可以说得通的。
  4. 确定遍历顺序:根据递推公式,从递归公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为i的状态是依靠 i之前节点数的状态。
  5. 举例推导dp数组,以n=4举例
    代码随想录算法训练营第41天| 343. 整数拆分 96.不同的二叉搜索树_第3张图片

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度:O(n)
class Solution {
    public int numTrees(int n) {
        //初始化 dp 数组
        int[] dp = new int[n + 1];
        //初始化0个节点和1个节点的情况
        dp[0] = 1;
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                //对于第i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加
                //一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-j
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
}

你可能感兴趣的:(代码随想录,leetcode,算法,算法)