題目來源:力扣
題目介紹:
給定一個整數 n,求以 1 … n 爲節點組成的二叉搜索樹有多少種?
示例:
輸入: 3
輸出: 5
解釋:
給定 n = 3, 一共有 5 種不同結構的二叉搜索樹.
審題:
考慮對於從到的節點序列,我們可以選擇到的任意一個值作爲二叉搜索樹根節點,假設選擇作爲根節點,則此時左子樹的有節點序列到構成,右子樹由節點到構成.假設我們節點序列到共有種不同形式的二叉搜索樹形式,節點序列到共有種不同形式的二叉搜索樹,則我們選擇作爲根節點,可以得到種不同的二叉搜索樹形式.而節點序列到可能構成的二叉搜索樹個數選擇不同根節點得到的二叉樹個數之和.
事實上,我們可以發現,該問題並不涉及最優性問題,因爲爲了計算所有可能的二叉樹種類,我們需要計算每一個可能的根節點位置選擇方案下,所可能產生的二叉搜索樹個數,在這一步中,並不存在最優選擇,而是將所有選擇結果相加.但由於子問題存在重疊,因此我們仍然可以考慮使用動態規劃算法解決該問題.
爲了求解長度爲的節點序列可能產生的二叉搜索樹總個數,我們需要已知所有長度小於的節點序列所可能產生的二叉搜索樹個數,此時的基礎情形便是長度爲1或長度爲0的情形.如果當前節點序列長度爲0或1,則此時的二叉搜索樹只有一種形式.
我們使用二維數組存儲每一狀態下,總方案.在該問題中,狀態包括節點序列的起點與終點表示節點序列所能構長的二叉搜索樹個數.
以上是我一開始面對該問題時所提出的方法, 後面我進一步思考發現,二叉搜索樹的可能個數與當前節點序列的值並無關係,僅與當前節點序列的長度有關.因此,我們可以進一步簡化動態規劃算法. 此時,當前的狀態變量爲節點序列的長度, 我們可以在每一步選擇左子樹的大小,其取值範圍從到. 我們使用一維數組S存儲當前狀態下二叉搜索樹可能個數, 我們可以得到如下狀態轉移方程:
java算法:
方法一
class Solution {
public int numTrees(int n) {
//S[i][j]表示以i...j-1爲節點組成的二叉搜索樹共有多少種
//如果根節點選爲k, 則S[i][k] + S[k+1][j]
int[][] S = new int[n+2][n+3];
//basecase
for(int i = 1; i < n+2; i++){
S[i][i] = 1;
S[i][i+1] = 1;
}
//如果計算長度爲k的節點所能組成的二叉樹, 我們應當計算確定所有長度小於k的節點序列所能組成的二叉樹
for(int len = 2; len <= n; len++){
for(int left = 1; left <= n-len+1; left++ ){
int right = left+len;
int total = 0;
for(int root = left; root < right; root++){
total += (S[left][root] * S[root+1][right]);
}
S[left][right] = total;
}
}
return S[1][n+1];
}
}
方法二:
class Solution {
public int numTrees(int n) {
int[] S = new int[n+1];
S[0] = 1;
S[1] = 1;
//如果計算長度爲k的節點所能組成的二叉樹, 我們應當計算確定所有長度小於k的節點序列所能組成的二叉樹
for(int len = 2; len <= n; len++){
int total = 0;
for(int leftSize = 0; leftSize < len; leftSize++){
total += S[leftSize] * S[len-leftSize-1];
}
S[len] = total;
}
return S[n];
}
}