LeetCode 96——不同的二叉搜尋樹
阿新 • • 發佈:2018-11-25
1. 題目
2. 解答
以 構建二叉搜尋樹,其中,任意數字都可以作為根節點來構建二叉搜尋樹。當我們將某一個數字作為根節點後,其左邊資料將構建為左子樹,右邊資料將構建為右子樹。因此,這是一個遞迴問題。
若以第 個數據為根節點,其左邊資料有 個,左子樹可能情況為 left_num,右邊資料有 個,右子樹可能情況為 right_num,因此以當前資料為根節點可以構建出 left_num * right_num 個二叉搜尋樹。
所以,我們要做的就是遍歷 ,統計出每個資料作為根節點可以構建出的二叉搜尋樹總個數即可。
- 遞迴法
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」!