题目描述
给定一个整数 $n$,返回所有结构不同的 BST (二叉搜索树),满足中序遍历是 $1 … n$。
样例
输入:3
输出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
解释:
上述对应的5个不同的二叉搜索树如下所示:
1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3
算法
(DFS) $O(C_{2n}^n/(n+1))$
递归搜索所有方案。
- 对于每段连续的序列 $l, l + 1, … r$,枚举二叉搜索树根节点的位置;
- 分别递归求出左右子树的所有方案;
- 左子树的任意一种方案和右子树的任意一种方案拼在一起,可以得到当前节点的一种方案,所以我们将左右子树的所有方案两两组合,并记录在答案中。
时间复杂度分析:我们来求一下当节点数是 $n$ 时,总共有多少种方案。假设方案数是 $h_n$,则有 $h_n = \sum_{k=0}^{n-1}h_k*h_{n-1-k}$。所以 $h_n$ 是卡特兰数,其通项公式是 $h_n = C_{2n}^n/(n+1)$。所以时间复杂度是 $O(C_{2n}^n/(n+1))$。
C++ 代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<TreeNode*> generateTrees(int n) {
if (!n) return vector<TreeNode*>();
return dfs(1, n);
}
vector<TreeNode*> dfs(int l, int r)
{
vector<TreeNode*>res;
if (l > r)
{
res.push_back(NULL);
return res;
}
for (int i = l; i <= r; i ++ )
{
vector<TreeNode*>left = dfs(l, i - 1)
, right = dfs(i + 1, r);
for (auto &lc : left)
for (auto &rc : right)
{
TreeNode *root = new TreeNode(i);
root->left = lc;
root->right = rc;
res.push_back(root);
}
}
return res;
}
};
tql
y总真的强,我完全想不到
y老师,问一下C2n 取 n, 当n趋近无穷大的时候,是趋于n!吗
哦,我好像想明白了,是2的n次方~
问下如果是用discussion里面的dp方法(https://leetcode.com/problems/unique-binary-search-trees-ii/discuss/31493/Java-Solution-with-DP),时间复杂度是怎么算的呀?
仍然是指数级别的,他的DP里藏了一个暴搜。
y总,请教一下,为什么每次递归的函数都要新开辟一个res,而全局开辟一个res传进去,会超时
搜索出的每一棵二叉树的节点是不能共用的,因为每个内部节点的左右儿子是不同的。如果共用会导致二叉树的结构错乱。
感觉输出数组样例不是完整的层遍历,例如 [1,null,2,null,3](3,3)后面应该还接了个NULL…真没看懂,大佬求解
对滴,leetcode的层序遍历会把最后连续的
null
忽略掉。TreeNode *root = new TreeNode(i);//这一句我写在了第一个循环的下面时,出错了,请问一下大佬这是为啥,
这里要对每种不同组合建立一个新的根节点,否则这些根节点的地址是一样的,那么它们的左右儿子在内层循环时会被不断覆盖掉。
好的,感谢大佬
昨天遇到同样问题,没想明白,感谢大佬的解答!
加油hh
妙啊hh遇到同样的问题,找了好久才发现,搞得我克隆了一颗二叉树。。
请问这个题目样例处的输出数组表示和树的对应关系应该怎么看?谢谢!
输出数组是整棵树的层序遍历。
从树根开始往下,一层一层遍历。对于每层,从左往右遍历父节点非空的节点,如果当前节点不存在,则输出null,否则输出节点的值。