題目
Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1...n.
For example,
Given n = 3, your program should return all 5 unique BST's shown below.
Input:
BST的最大值(BST范圍1-n):: Integer
Output:
由1~ n組成的所有BST :: List<TreeNode>
Recursion 解法
Intuition:
這道題吶,其實Recursion的想法很容易想,我們依次遍歷每個數,把這個數當初Root,那么比這個數小的數都當成左子樹,比這個數大的數都當成右子樹。退出Recursion的條件自然是左邊界大于右邊界啦。
Edge Case:
if (n <= 0){
return new ArrayList<TreeNode>();
}
Code:
public List<TreeNode> UniBST(int n){
//edge cases
if (n <= 0){
return new ArrayList<TreeNode>();
}
return helper(1, n);
}
public List<TreeNode> helper(int left, int right){
List<TreeNode> res = new ArrayList<>();
if (left > right){
res.add(null);
return res;
}
for (int i = 1; i <= n; i++){
List<TreeNode> left = helper(left, i - 1);
List<TreeNode> right = helper(i + 1, right);
for (TreeNode l: left){
for (TreeNode r: right){
TreeNode root = new TreeNode(i);
root.left = l;
root.right = r;
res.add(root);
}
}
}
return res;
}
DP 解法
但是這道題居然還可以用DP解而且解法比較匪夷所思...
首先,根據DP的本質是當前問題是基于之前的子問題來解決的。那么這道題我們是想建一個擁有n個node的樹,很自然的想到能不能在利用n-1個node的樹。
一步步來哈,左邊的樹好想啊, 1 到 n-1 的數組成的樹肯定每個node的值都比n小啊,那這個樹可以直接拿來安到左邊當做子樹就好。
Tricky的部分在于我們怎么通過n-1個node建成的樹來得出n個node樹的右子樹呢?最明顯的問題就是值不夠大呀。怎么辦?沒有條件創造條件也要上啊( ̄︶ ̄)↗, 我們不是想右邊子樹的值都大于n么?那么就直接在之前已經建好的某個樹所有的node都加n就好了。
還有要注意的一點就是子樹的選擇要確保他們node的個數加1(root)是等于n的。
和所有的DP一樣,我們需要有個DP array, 而因為我們依賴于n-1個node的樹(可能不止一種),那么這個array里面存的就應該是List<TreeNode>,即
List<TreeNode>[] dp = new List<TreeNode>[n + 1];
起始狀態 是0 個node,沒法建樹,那么index為0對應的那個List<TreeNode>里放 null
是不是還是一臉懵逼???沒關系我們跑個??
來看看n = 3的情況吧
n = 1 和 n = 2的情況比較trivial,我們跳過, 假設0 ~ 2 的dp array我們已經填好了,應該是這個樣子滴:
那么此時我們可以選擇可以作為子樹的的root范圍是0 ~ 2
意義上可以表示為:
要建一個3個node的樹,假如取一個含0個node的樹作為左子樹 即為dp[0]里存的樹, 新的root的值應該比左子樹最大值大1, 即為1。 右子樹取剩下的node, 即為 3 - 0 - 1 = 2, 也就是dp[2]里存的樹, 但是右子樹我們還要進一步處理讓他比新的root大,那么加offset 1,注意這是dp[2]里已經有兩個樹。最終得到的樹為也有兩種可能:
Code:
TC: O(n^4) SC: O(n^2)?
public List<TreeNode>UniqueBST(int n){
if (n == 0){
return new ArrayList<TreeNode>(null);
}
//initialize
List<TreeNode>[] dp = new List<TreeNode>[n + 1];
dp[0].add(null);
for (int i = 1; i <= n; i++){ //select total number of nodes in a tree
dp[i] = new ArrayList<>();
for (int j = 0; j < i; j++){ //select number of nodes in left subtree
for (TreeNode l: dp[i]){
for (TreeNode r: dp[i - 1 - j]){ //select number of nodes in right subtree
TreeNode newRoot = new TreeNode(j + 1);
newRoot.left = l;
newRoot.right = addOffset(r, j + 1);
dp[i].add(newRoot);
}
}
}
}
return dp[n];
}
public TreeNode addOffset(TreeNode root, int offset){
if (root == null){
return null;
}
TreeNode res = new TreeNode(root.val + offset);
res.left = addOffset(root.left, offset);
res.right = addOffset(root.right, offset);
return res;
}
Reference
https://discuss.leetcode.com/topic/2940/java-solution-with-dp
https://leetcode.com/problems/unique-binary-search-trees-ii/description/