(40/60)整數拆分、不同的二叉搜尋樹

Tazdingo發表於2024-03-09

漸入佳境

整數拆分

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];
};

相關文章