95. Unique Binary Search Trees II**
https://leetcode.com/problems/unique-binary-search-trees-ii/
题目描述
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
C++ 实现 1
可以先了解一下 96. Unique Binary Search Trees** 这道题. 相比前一道题, 此题要求找出所有的 Unique BST. 下面代码中, 使用 generate(start, end)
来产生 [start .... end]
范围内的 BST. 思路是, 先用 [start ..., i - 1]
产生左子树, 使用 [i + 1, ..., end]
产生右子树, 最后保存产生的结果. 由于 [i + 1, ..., end]
的值始终要比 [start, ..., i - 1]
中的值大, 所有这符合 BST 的性质, 即所有的右子树节点值要大于左子树节点值. 这是个递归问题, 所以任何子树都满足这条性质.
class Solution {
private:
vector<TreeNode*> generate(int start, int end) {
if (start > end) return {nullptr};
vector<TreeNode*> res;
for (int i = start; i <= end; ++ i) {
auto ltree = generate(start, i - 1);
auto rtree = generate(i + 1, end);
for (auto &lc : ltree) {
for (auto &rc : rtree) {
TreeNode *root = new TreeNode(i);
root->left = lc;
root->right = rc;
res.push_back(root);
}
}
}
return res;
}
public:
vector<TreeNode*> generateTrees(int n) {
if (n <= 0) return {};
return generate(1, n);
}
};
C++ 实现 2
两年前的代码… 我现在来看简直震惊 🤪🤪🤪, 还好当时做了笔记, 否则现在要弄懂的成本太高了. 记录如下:
思路: 要解决这道题, 可以使用动态规划的方法. 如果我们知道了保存 1 ~ n - 1
的所有 BST, 假设为 f(n - 1)
, 那么当考虑 n
时, 就要判断怎样将 n 插入到 f(n - 1)
中的每棵 BST 中, 这样就能得到 f(n)
了. 举个例子, 比如 n = 3
时, 上面可以产生 5 棵 BST, 那这 5 棵 BST 是怎么得到的呢? 先考虑 f(2)
.
如果 n = 2
时, 我们可以如下的 BST:
1 2
\ /
2 1
即 f(2) 时产生了两棵 BST.
情况 1: 那么当要插入 n = 3
时, 由于 n 总比 f(n - 1)
中每棵 BST 中的最大值(即 n-1
) 都要大, 那么可以将那些 BST 作为 n 的左子树, 即:
3 3
/ /
1 2
\ /
2 1
情况二: 可以将 n 作为 f(n - 1)
中每棵 BST 的右子树, 但这里有个问题要注意, 先看例子:
1 2
\ / \
2 1 3
\
3
但是如何得到下面这个呢?
1
\
3
/
2
这时, 我们就知道了, 如果将 3 沿着根节点的右子节点一直走, 如果右子节点为空, 那么就将 3 插入进去; 如果右子节点不为空, 那么就需要将 3 插入到右子节点的上面(所谓"上面"自己体会), 并将右子节点以及后面的内容作为 3 的左子树. 根据这个思路, 我们从根节点开始, 依次判断右子节点是否为空, 然后插入节点 3. 按照顺序画图如下:
1 1 2
\ \ / \
3 2 1 3
/ \
2 3
这样就得到了 5 个不同的 BST.
class Solution {
private:
// 由于最后的结果是返回所有的不同子树, 那么在求 f(n) 的
// 时候, 需要拷贝 f(n - 1) 中的树, 再进行操作. 拷贝使用
// 前向遍历即可.
TreeNode* copyTree(TreeNode *root) {
if (!root)
return nullptr;
TreeNode *newroot = new TreeNode(root->val);
newroot->left = copyTree(root->left);
newroot->right = copyTree(root->right);
return newroot;
}
public:
vector<TreeNode*> generateTrees(int n) {
if (n < 1)
return vector<TreeNode*>{};
if (n == 1)
return vector<TreeNode*>{new TreeNode(n)};
auto treeSet = generateTrees(n - 1);
vector<TreeNode*> res;
for (auto &subroot : treeSet) {
TreeNode *root = new TreeNode(n);
// 情况 1: 将 f(n - 1) 中的所有BST 作为 root 的左子树
root->left = subroot;
res.push_back(root);
// 情况 2:
// 使用 ptr2 来记录 root 应插入的位置
// 另一方面还要用 ptr 来记录产生下一棵BST时, ptr2 要移动的位置
// 这个可以画图体会.
auto ptr = subroot;
while (ptr) {
TreeNode *node = new TreeNode(n);
TreeNode *root = copyTree(subroot);
auto ptr2 = root;
while (ptr2->val != ptr->val)// 使ptr2移动到和 ptr 对应的位置
ptr2 = ptr2->right;
auto temp = ptr2->right;
ptr2->right = node;
// 将 temp 作为 n 的左子树.
node->left = temp;
res.push_back(root);
ptr = ptr->right;
}
}
return res;
}
};