1. 程式人生 > >LeetCode 96——不同的二叉搜索樹

LeetCode 96——不同的二叉搜索樹

solution 執行 {0} 只需要 獲取 問題 需要 += 奇數

1. 題目

技術分享圖片

2. 解答

\(1, 2, \cdots, n\) 構建二叉搜索樹,其中,任意數字都可以作為根節點來構建二叉搜索樹。當我們將某一個數字作為根節點後,其左邊數據將構建為左子樹,右邊數據將構建為右子樹。因此,這是一個遞歸問題。

若以第 \(i\) 個數據為根節點,其左邊數據有 \(i-1\) 個,左子樹可能情況為 left_num,右邊數據有 \(n-i\) 個,右子樹可能情況為 right_num,因此以當前數據為根節點可以構建出 left_num * right_num 個二叉搜索樹。

所以,我們要做的就是遍歷 \(i = 1\cdots n\),統計出每個數據作為根節點可以構建出的二叉搜索樹總個數即可。

  • 遞歸法
class Solution {
public:
        
       int numTrees(int n) {

        int sum = 0;

        if (n <= 1) return 1;

        // 以當前的數為根節點,左右兩邊的數分別構建子樹
        for (int i = 1; i <= n; i++)
        {
            int left_num = numTrees(i - 1); // 左邊的數可以構建多少個二叉搜索樹

            int right_num = numTrees(n - i); // 右邊的數可以構建多少個二叉搜索樹

            sum += left_num * right_num;
        }

        return sum;
    }
};

但是上面的程序運行時超時了,其實我們只需要統計一半數據就可以了,因為兩邊是對稱的。

比如我們有 1,2,3,4,5 五個數,以 2 作為根節點,左邊有 1 個數,右邊有 3 個數。以 4 作為根節點,左邊有 3 個數,右邊有 1 個數。這兩種情況是一樣的,因此如果數據個數為偶數,我們只需要統計一半數據即可,而為奇數的話我們就要再多統計一個中間數據。

class Solution {
public:
    
    int numTrees(int n) {
        
        int sum = 0;
          
        if (n <= 1) return 1;
        
        int is_odd = n % 2;
        int mid = n / 2;
        
        // 以當前的數為根節點,左右兩邊的數分別構建子樹
        for (int i = 1; i <= mid; i++)
        {
            int left_num = numTrees(i - 1); // 左邊的數可以構建多少個二叉搜索樹
            
            int right_num = numTrees(n - i); // 右邊的數可以構建多少個二叉搜索樹
            
            sum += left_num * right_num;
        }
        
        sum = sum * 2;
        
        if (is_odd) sum = sum + numTrees(mid) * numTrees(n - mid - 1);
            
        return sum;
    }
};

此外,我們還可以定義一個全局變量,來存放已經計算過的數值,避免在遞歸過程中大量地重復計算。

class Solution {
public:
    
    #define MAX 1000
    
    int nums[MAX]; // 存放已經計算過的數值

    int numTrees(int n) {

        int sum = 0;

        //if (n <= 0) return 1;
        if (n <= 1) return 1;

        // 以當前的數為根節點,左右兩邊的數分別構建子樹
        for (int i = 1; i <= n; i++)
        {
            if (nums[i-1] == 0) nums[i-1] = numTrees(i - 1); // 左邊的數可以構建多少個二叉搜索樹
            int left_num = nums[i-1];

            if (nums[n-i] == 0) nums[n-i] = numTrees(n - i); // 右邊的數可以構建多少個二叉搜索樹
            int right_num = nums[n-i]; 

            sum += left_num * right_num;
        }

        return sum;
    }
};
  • 叠代法
    還可以將遞歸改寫為循環,避免函數多次調用執行效率較低。
class Solution {
public:
    
    int numTrees(int n) {

        int nums[n+1] = {0};

        nums[0] = 1;
        nums[1] = 1;
       
        if (n <= 1) return 1;
        
        for (int i = 2; i <= n; i++)
        {
            // 從 n=2 開始統計可以構建多少個不同的二叉搜索樹
            for (int j = 1; j <= i; j++)
            {
                nums[i] += nums[j-1] * nums[i-j];
            }
        }

        return nums[n];
    }
};      

獲取更多精彩,請關註「seniusen」!
技術分享圖片

LeetCode 96——不同的二叉搜索樹