不说废话,直戳盲点,打通思路, 举一反三。
题目描述
给定一个整数 n,生成所有由 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii
先上代码
执行用时 :28 ms, 在所有 C++ 提交中击败了90.78%的用户
内存消耗 :17.3 MB, 在所有 C++ 提交中击败了23.48%的用户
/**
* 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 <= 0)
return vector<TreeNode*>();
return generateTrees(1,n);
}
vector<TreeNode*> generateTrees(int start, int end){
//递归函数
vector<TreeNode *> res;
if(start > end)
{// 递归函数出口
res.push_back(nullptr);
}
else{
for(int i=start; i<=end; ++i){//每一个数字都可以作为根
//左子树集合,从start->i之间递归生成
vector<TreeNode*> leftTrees = generateTrees(start,i-1);
//右子树集合,从i->end之间递归生成
vector<TreeNode*> rightTrees = generateTrees(i+1,end);
for(auto leftTree : leftTrees){
for(auto rightTree : rightTrees){
//左子树右子树两两组合,构成一个解。
TreeNode* tree = new TreeNode(i);
tree->left = leftTree;
tree->right = rightTree;
res.push_back(tree);
}
}
}
}
return res;
}
};
算法流程
总结来说,有以下几个要点:
- 递归思路:
- 定义递归执行体:generateTrees(int start, int end)
- 参数含义:由start->end 表示一个有序数列。
- 递归执行体功能:在这个有序数列上,生成二叉搜索树集合。
- 在给定有序数列上,依次将每个元素(下标为 i )做为根。考察当这个元素作为根时,可以获得几种二叉搜索树:
- 考察左子树有几种,调用递归执行体,用start->i-1作为构造左子树的有序数列,保证满足二叉搜索树性质。
- 考察右子树有几种,调用递归执行体,用i+1->end作为构造右子树的有序数列,保证满足二叉搜索树性质。
- 每种左子树和每种右子树结合当前的根节点,可组成一个解,将其加入解集。(左子树并不会与右子树冲突,因为参数代表的有序数列是互斥的)
- 定义递归执行体:generateTrees(int start, int end)
- 递归函数出口:start >= end, 有序数列为空时,当前子树为空,递归函数开始上升。
学习和思考
- 用两个下标start, end表示有序数列,节约空间的同时方便递归调用。
- 如何递归的在有序数列上构造二叉搜索树集合。
- 思考:如果序列是随机的,如何去构造二叉搜索树集合呢?
- 对序列进行排序。
- 参数:排序后的数列,start, end。
- 后续过程与上述过程类似。