中序遍历对BST非常重要,之前也讲过了BST的基本操作。(东哥总结的是真的强)
不同的二叉搜索树
给定一个正整数n,请你计算,存储{1,2,3...n}这些值共有多少种不同的BST结构。
还是那句老话,二叉树算法的关键在于明确根节点需要做什么。
假设给算法输入5,也就是说用{1,2,3,4,5}这些数字去构造BST。
首先,这棵BST的根节点总共有几种情况?
显然有5中,因为1-5都可以作为BST的根节点对吧。然后我们固定住一个根节点,然后再进行分析,这个时候有几种不同的BST呢?
比如树把3固定住,去分析其他树节点都可能的情况。
根据BST的性质,根节点的左子树都比根节点的值小,右子树的值都比根节点的值大。所以如果固定3为根节点,左子树节点就是{1,2}的组合,右子树就是{4,5}的组合。
左子树的组合数和右子树的组合数乘积就是3为根时的BST个数。
上面讲的是以3为根的情况,其他节点也是一样的。
[1,2]和[4,5]有几种组合呢?
排列组合公式 2*2=4种,也就是说我们的结果就是左右子树组合的乘积。
使用递归函数
//闭区间[lo,hi]的数字能组成count(lo,hi)种BST
int count(int lo,int hi);
根据这个函数的定义,结合刚才的分析,可以写出代码
//主函数
int numTrees(int n){
//计算闭区间[1,n]组成的BST的个数
return count(1,n);
}
//计算闭区间[lo,hi]组成的BST个数
int count(int lo,int hi){
//base case
if(lo>hi){
return 1;
}
int res=0;
for(int i=lo;i<=hi;i++){
//i的值作为根节点root
int left=count(lo,i-1);
int right=count(i+1,hi);
//左右子树的组合数乘积是BST的总数
res+=left*right;
}
}
利用动态规划中的技巧消除重叠子问题,添加一个备忘录
//备忘录
int [][] memo;
int numTrees(int n){
//备忘录的值初始化为0
memo=new int[n+1][n+1];
return count(1,n);
}
int count(int lo,int hi){
if(lo>hi)return 1;
//检查备忘录
if(memo[lo][hi]!=0){
return memo[lo][hi];
}
int res=0;
for(int mid=lo;mid<=hi;mid++){
int left=count(lo,mid-1);
int right=count(mid+1,hi);
res+=left*right;
}
//将结果存入备忘录
memo[lo]][hi]=res;
return res;
}
再延伸一下,力扣上的95题要求返回不同的BST
1.穷举root节点的所有可能。
2.递归构造出左右子树的所有合法BST。
3.给root节点穷举所有左右子树的组合。
public List<TreeNode> generateTree(int n){
if(n==0) return new LinkedList<>();
//构造闭区间[1,n]组成的BST
return build(1,n);
}
//构造闭区间[lo,hi]组成的BST
List<TreeNode> build(int lo,int hi){
List<TreeNode> res=new LinkedList<>();
//base case
if(lo>hi){
res.add(null);
return res;
}
//1.穷举root节点的所有可能
for(int i=lo;i<=hi;i++){
//2.递归构造出左右子树的所有合法BST
List<TreeNode> leftTree=build(lo,i-1);
List<TreeNode> rightTree=build(i+1,hi);
//3.给root节点穷举所有左右子树的组合
for(TreeNode:leftTree){
for(TreeNode:rightTree){
//i作为根节点root的值
TreeNode root=new TreeNode(i);
root.left=left;
root.right=right;
res.add(root);
}
}
}
return res;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。