Given an integer n, generate all structurally unique BST’s (binary search trees) that store values 1 … n.
Example:
Input: 3
Output:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
Explanation:
The above output corresponds to the 5 unique BST’s shown below:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
这道题是96题的变体,问从1到n的数能组成多少种BST,并且输出具体的BST
96题只需要知道有多少种情况,所以用dp解决,只需要知道选择root的左子树有多少个节点,右子树有多少个节点。
而这道题只知道左子树右子树有多少个节点还不行,还需要知道具体的排列方式,但是还是可以沿用96的思路,就是选一个root,拆分左子树和右子树
方法一(递归):
从1到n的数从左到右选一个作root,然后左边的部分用来构成左子树,右边部分构成右子树
递归:
左子树中选一个作root,拆分左子树和右子树
右子树中选一个作root,拆分左子树和右子树
结束条件:
start点 > end点
比如
[1,2,3] 选1作root,1的index是0
index 0左边部分就是(0, index-1)即(0, 0-1), 右边是(index+1, 1)即(0+1,2)
可以看到左边start是0 > end的-1,所以返回空
左右子树都是空的时候,也就是只有一个节点的时候,把这个节点作root返回
左右都不是空,就是前面的BST的个数=左子树个数 * 右子树个数
所以左子树每一种情况都要遍历右子树所有的情况
代码:
public List<TreeNode> generateTrees(int n) {
//一定要单独处理,不然会返回一个二维的空list,而不是一维了
if(n < 1) {
return new ArrayList<>();
}
return generateTrees(1, n);
}
public List<TreeNode> generateTrees(int l, int r) {
List<TreeNode> result = new ArrayList<>();
if(l > r) {
result.add(null); //一定要add null
return result;
}
for(int i = l; i <= r; i++) {
for(TreeNode left : generateTrees(l, i - 1)) {
for(TreeNode right : generateTrees(i + 1, r)) {
TreeNode root = new TreeNode(i);
root.left = left;
root.right = right;
result.add(root);
}
}
}
return result;
}
方法二(DP):
参考链接中的DP方法,加上一些理解:
长度为n+1的DP数组,其中dp[i] 表示1~i 所有的unique BST, 即记住1 ~ n的所有BST
那么dp[i]应该是保存BST的list
选中一个i, 遍历0到 i 的每个点 j ,取j + 1为root,root左边的 j 个元素组成的BST构成左子树,即左子树为dp[ j ]中的所有元素,j+1到 i 的 i - j - 1个元素构成右子树,可用dp[i - j - 1]的BST的结构表示右子树,只能用结构,什么意思
比如1,2, 3, 4, 5,取 3 为root,左边的1,2组成的BST为左子树,那么两个元素组成BST的结构就已经确定了,右边4,5构成的BST的结构和1,2构成的BST结构是一样的,只是值不同而已
这里root = j + 1 = 3, 左子树为dp[2], 右子树的结构也为dp[2], 即dp[i - j - 1] = dp[5 - 2 - 1] = dp[2], 但是需要调整一下值,即把1,2改为4,5,可以看到1,2 + (root处的j+1即3)即为4,5
这样可以保存1~i 构成的所有BST,最后返回dp[n]即可
public List<TreeNode> generateTrees(int n) {
if(n < 1) {
return new ArrayList<TreeNode>();
}
List<TreeNode>[] dp = new List[n+1];
dp[0] = new ArrayList<TreeNode>();
dp[0].add(null);
for(int i = 1; i <= n; i++) {
dp[i] = new ArrayList<TreeNode>();
for(int j = 0; j < i; j++) {
for(TreeNode left : dp[j]) {
for(TreeNode right : dp[i - j - 1]) {
TreeNode root = new TreeNode(j+1);
root.left = left;
root.right = addVal(right, j+1);
dp[i].add(root);
}
}
}
}
return dp[n];
}
public TreeNode addVal(TreeNode rt, int val) {
if(rt == null) {
return null;
}
TreeNode root = new TreeNode(rt.val + val);
root.left = addVal(rt.left, val);
root.right = addVal(rt.right, val);
return root;
}
联想96题:给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
也是DP,只是不需要具体的BST,只需要数量,用DP数组保存数量,dp[i]也是表示从1到i 有多少种,
然后j 从0遍历到i,以j+1为root,dp[j] 为左子树种数,dp[i - j - 1]为右子树种树,dp[i ] += dp[j ] * dp[i - j - 1]
public int numTrees(int n) {
if(n < 1) {
return 0;
}
int[] dp = new int[n+1];
dp[0] = 1;
dp[1] = 1;
for(int i = 2; i <= n; i++) {
for(int j = 0; j < i; j++) {
dp[i] += dp[j] * dp[i - j - 1];
}
}
return dp[n];
}