很复杂的一道题,做题时状态好,加上刚做了普通Unique Binary Search Trees动态规划的解法还热着,很快就找到了以下的思路
但这道题比只要求数量的还是难很多,写起来很复杂,花了很久时间
【本题注意】
本题中List<TreeNode>存储的每一个TreeNode,都是一颗树的根节点,代表一整棵树
我们往一个list里面加node的时候,也要将它构造成BST的根节点
【自己的代码思路】
动态规划为基础思想,用dp[i]存储i个节点能组成的所有BST的根节点
计算dp[i]的方法是循环将1~i的任意节点k确定为根节点,然后组合dp[k - 1]和dp[i - k]中存储的左右子树形态,得到dp[i]的所有树
这里还需要注意的是:
右子树的节点值是大于dp[i - k]中树的节点值的,所以dp[i - k]中的树不能直接作为右子树,而是必须拷贝每一棵dp[i - k]中树的形态,并将值加上一个offset,创建得到右子树
左子树是可以直接使用dp[k - 1]的
/**
* 自己的代码
* (最开始深拷贝是复制来的,因为这题代码量太大了,怕思路错了白费太多功夫,又怕深拷贝写错了加大debug难度,通过之后自己写了深拷贝)
* Runtime: 1 ms, faster than 92.36%
* Memory Usage: 39.1 MB, less than 91.95%
*/
class Solution {
public List<TreeNode> generateTrees(int n) {
List<TreeNode>[] dp = new LinkedList[n + 1];
dp[0] = new LinkedList<>();
dp[0].add(null);
for (int i = 1; i <= n; i++) {
dp[i] = new LinkedList<>();
for (int j = 0; j < i; j++)
newList(dp[j], dp[i - 1 - j], j + 1, dp[i]);
}
return dp[n];
}
// 将各个左子树、右子树组合成以值为val的节点为根节点的树,加入dp[i],循环完成得到dp[i]
private void newList(List<TreeNode> left, List<TreeNode> right, int val, List<TreeNode> curr) {
for (TreeNode tl : left)
for (TreeNode tr : right) {
TreeNode trCopy = tr == null ? null : copyTree(tr, val); // 右子树的应该有的节点值比dp中对应树的大,所以需要进行深拷贝生成一颗val = val + offset的新树作为右子树;左子树不用
TreeNode nodeJ = new TreeNode(val, tl, trCopy);
curr.add(nodeJ);
}
}
// 深拷贝一棵和root一样的树,原树的节点val + offset = 新树的节点val
private TreeNode copyTree(TreeNode n, int offset) {
if (n == null)
return null;
TreeNode node = new TreeNode(n.val + offset);
node.left = copyTree(n.left, offset);
node.right = copyTree(n.right, offset);
return node;
}
}