题目描述
给定一个整数 n,生成所有由 1 … n 为节点所组成的二叉搜索树。
总结
SDC1:递归
SDC2:动态规划,同时还用了同构。
值得注意的是,所有的左子树没有 clone ,也就是很多子树被共享了,在内存中就会是下边的样子。
代码写得很巧妙,在left,right的地方取到的左右子树不会违反搜索树的规则。
也就是左子树用的都是之前的子树,没有开辟新的空间。
SDC3:动态规划
仔细分析,可以发现一个规律。首先我们每次新增加的数字大于之前的所有数字,所以新增加的数字出现的位置只可能是根节点或者是根节点的右孩子,右孩子的右孩子,右孩子的右孩子的右孩子等等,总之一定是右边。其次,新数字所在位置原来的子树,改为当前插入数字的左孩子即可,因为插入数字是最大的。
Sample & Demo Code 1
class Solution {
public List<TreeNode> generateTrees(int n) {
if (n == 0) return new LinkedList<TreeNode>();
return generate_trees(1, n);
}
private LinkedList<TreeNode> generate_trees(int start, int end) {
LinkedList<TreeNode> all_trees = new LinkedList<TreeNode>();
if (start > end) {
all_trees.add(null);
return all_trees;
}
for (int i = start; i <= end; i++) {
LinkedList<TreeNode> left_trees = generate_trees(start, i - 1);
LinkedList<TreeNode> right_trees = generate_trees(i + 1, end);
for (TreeNode le : left_trees)
for (TreeNode ri : right_trees) {
TreeNode current_tree = new TreeNode(i);
current_tree.left = le;
current_tree.right = ri;
all_trees.add(current_tree);
}
}
return all_trees;
}
}
Sample & Demo Code 2
class Solution {
public List<TreeNode> generateTrees(int n) {
List<TreeNode>[] dp = new ArrayList[n+1];
dp[0] = new ArrayList<TreeNode>();
if (n == 0) return dp[0];
// dp 存储的是不同长度的树,dp[4] 存储的是所有长度为4的树
dp[0].add(null);
for(int len = 1; len <= n; len++) { // 长度1到n
dp[len] = new ArrayList<>();
for(int root = 1; root <= len; root++) { // 将不同的树作为根节点
int left = root - 1; // 左子树长度
int right = len - root; // 右子树长度
for (TreeNode leftTree : dp[left]) { // 和下面一句 for 组成笛卡尔积
for (TreeNode rightTree : dp[right]) {
TreeNode rootTree = new TreeNode(root);
rootTree.left = leftTree;
rootTree.right = clone(rightTree, root); // root的值就为offest的量
dp[len].add(rootTree);
}
}
}
}
return dp[n];
}
private TreeNode clone(TreeNode n, int offest) {
if(n == null) return null;
TreeNode res = new TreeNode(n.val + offest);
res.left = clone(n.left, offest);
res.right = clone(n.right, offest);
return res;
}
}
Sample & Demo Code 3
class Solution {
public List<TreeNode> generateTrees(int n) {
List<TreeNode> pre = new ArrayList<>();
if(n == 0) return pre;
pre.add(null);
for(int i = 1; i <= n; i++) {
List<TreeNode> cur = new ArrayList<>();
for(TreeNode root : pre) { // 遍历之前的所有解
// 新节点为根节点的情况
TreeNode newNode = new TreeNode(i);
// newNode.left = copyNode(root); 这种写也可以,不过比较浪费空间(?)
newNode.left = root;
cur.add(newNode);
// 新节点为子节点的情况
for(int j = 0; j <= i; j++) { // 插入到右孩子,右孩子的右孩子...最多找 n 次孩子
TreeNode root_copy = copyNode(root);// 复制当前的树
TreeNode tempR = root_copy;
for(int k = 0; k < j; k++) { // 找到当前循环需要插入的位置
if(tempR == null) break;
tempR = tempR.right;
}
if(tempR == null) break;
TreeNode tempL = tempR.right; // 保存新节点的左孩子
newNode = new TreeNode(i); // 创建新节点
tempR.right = newNode; // 插入的新节点
newNode.left = tempL; // 插入新节点的左孩子
cur.add(root_copy); // 添加到结果集
}
}
pre = cur;
}
return pre;
}
private TreeNode copyNode(TreeNode root) {
if(root == null) return null;
TreeNode res = new TreeNode(root.val);
res.left = copyNode(root.left);
res.right = copyNode(root.right);
return res;
}
}
作者:windliang
链接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-2-7/
来源:力扣(LeetCode)