以 $1, 2, \cdots, n$ 構建二叉搜索樹,其中,任意數字均可以做爲根節點來構建二叉搜索樹。當咱們將某一個數字做爲根節點後,其左邊數據將構建爲左子樹,右邊數據將構建爲右子樹。所以,這是一個遞歸問題。函數
若以第 $i$ 個數據爲根節點,其左邊數據有 $i-1$ 個,左子樹可能狀況爲 left_num,右邊數據有 $n-i$ 個,右子樹可能狀況爲 right_num,所以以當前數據爲根節點能夠構建出 left_num * right_num 個二叉搜索樹。spa
因此,咱們要作的就是遍歷 $i = 1\cdots n$,統計出每一個數據做爲根節點能夠構建出的二叉搜索樹總個數便可。code
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 個數。這兩種狀況是同樣的,所以若是數據個數爲偶數,咱們只須要統計一半數據便可,而爲奇數的話咱們就要再多統計一箇中間數據。rem
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; } };
此外,咱們還能夠定義一個全局變量,來存放已經計算過的數值,避免在遞歸過程當中大量地重複計算。it
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」! io