(40/60)整数拆分、不同的二叉搜索树

渐入佳境

整数拆分

leetcode:343. 整数拆分

动态规划

思路

  1. 意义:i拆分乘积的最大值为dp[i]

  2. 递推:dp[i] = max{dp[i],j*(i-j),j*dp[i-j]}

  3. 初始化:从2开始可以拆,有意义,dp[2] = 1,其他为0

  4. 遍历顺序:

    for(int i = 3;i <= n;i++) 	// 0、1没意义,2已经赋值了,从3开始
                for(int j = 1;j <= i/2;j++)	// 拆出0没有意义,从1开始。取到i/2就可以,再往后重复。
    

复杂度分析

时间复杂度:O(N^2)。

空间复杂度:O(N)。

注意点

代码实现

class Solution {
public:
    /*
    i拆分乘积最大为dp[i]
    dp[i] = max{dp[i],j*(i-j),j*dp[i-j]}
    dp[0] = 0;dp[1] = 0;dp[2] = 1;
    i: 3~n  j: 1~i/2
    */
    int integerBreak(int n) {
        vector<int> dp(n + 1,0);	// 数组大小比最大下标大1
        dp[2] = 1;
        for(int i = 3;i <= n;i++){
            for(int j = 1;j <= i/2;j++){
                dp[i] = max(dp[i],max(j * (i-j),j * dp[i-j]));
            }
        }
        return dp[n];
    }
};

TypeScript:

/*
    i拆分乘积最大为dp[i]
    dp[i] = max{dp[i],j*(i-j),j*dp[i-j]}
    dp[0] = 0;dp[1] = 0;dp[2] = 1;
    i: 3~n  j: 1~i/2
    */

function integerBreak(n: number): number {
    let dp:number[] = new Array(n + 1).fill(0);
    dp[2] = 1;
    for(let i = 3;i <= n;i++){
        for(let j = 1;j <=i/2;j++){
            dp[i] = Math.max(dp[i],j * (i-j),j * dp[i-j]);
        }
    }
    return dp[n];
};

不同的二叉搜索树

leetcode:96. 不同的二叉搜索树

动态规划

思路

通过画图找到了规律,i个节点就是一个节点的基础上,分配i-1个节点在左右子树的问题

  1. 意义:i个节点有dp[i]种不同的二叉搜索树

  2. 遍历和递推:dp[i] = dp[j] + dp[i-1-j]

    for(int i = 2;i <= n;i++){
        for(int j = i-1;j >= i/2;j--){  
            // i-1拆成j和i-1-j
            if(j == i - 1 - j)  // 拆出两个相等数时不乘2(轴对称)
                dp[i] += dp[j] * dp[i-1-j];
            else    // 拆出不对称时,有对称的两个解
                dp[i] += 2 * dp[j] * dp[i-1-j];
        }
    }
    
  3. 初始化:dp[0] = 1;dp[1] = 1; dp[0]没有意义但是需要赋值为1以保持递推公式的一致。

复杂度分析

时间复杂度:O(N^2)。

空间复杂度:O(N)。

注意点

  1. JS数字除法会变成小数,

代码实现

class Solution {
public:
    /*
    本质上就是求n个节点的二叉搜索树可能的形状数
    */
    int numTrees(int n) {
        vector<int> dp(n + 1,0);
        dp[0] = 1;dp[1] = 1;
        for(int i = 2;i <= n;i++){
            for(int j = i-1;j >= i/2;j--){  
                // i-1拆成j和i-1-j
                if(j == i - 1 - j)  // 拆出两个相等数时不乘2(轴对称)
                    dp[i] += dp[j] * dp[i-1-j];
                else    // 拆出不对称时,有对称的两个解
                    dp[i] += 2 * dp[j] * dp[i-1-j];
            }
        }
        return dp[n];
    }
};

TypeScript:

function numTrees(n: number): number {
    let dp:number[] = new Array(n + 1).fill(0);
    dp[0] = 1; dp[1] = 1;
    for(let i = 2;i <= n;i++){
        for(let j = i-1;j >= Math.floor(i/2);j--){
            // i-1拆成j和i-1-j
            if(j === i-1-j){
                dp[i] += dp[j] * dp[i-1-j]; // 拆出两个相等数时不乘2(轴对称)
            }
            else{    // 拆出不对称时,有对称的两个解
                dp[i] += 2 * dp[j] * dp[i-1-j];
            }
        }
    }
    return dp[n];
};
posted @ 2024-03-09 00:02  Tazdingo  阅读(111)  评论(0)    收藏  举报