二叉查找树与树的递归
引言
在上一篇文章中,主要介绍了二叉树的遍历相关以及相关联的算法
这一篇也是系列的第二篇:二叉查找树
相关的一些较为经典的力扣问题。在算法第四版中,对于树的介绍将其二叉查找树,与平衡查找树归于查询
一章节中。对于平衡二叉树其查找的效率也是显而易见的,这里就不再过多展开。主要系列主线集中在对力扣的算法上面,具体的算法思想以及基础内容移步算法图书电子版。下面开始系列的问题的解决。
96-Unique Binary Search Trees
题目:
给定一个值n,能构建出多少不同的值包含1…n的二叉搜索树(BST:就是对于一个节点来说,其左子树都会小于根节点,右子树都会大于根节点)?
例如给出n=3;有以下题解:
此时我们将其变换一个位置得到以下情况:
此时我们可以看到的是对于根节点是1的时候:等于其左子树的排列组合的个数与右子树排列组合的个数进行相乘,对于1来说,2,3都大于其,所以其右子树的排列组合为全零–也就是说只有一种情况,但是对于右边来说,右子树有2,3。但是对于2,3 来说此时又是分为两种情况,要么是2是根节点,要么3是根节点,所以最后规划来得到的是可以使用动态规划来进行题解:以此类推得到:当一个数组为1,2,3,……n
时候,基于以下原则进行构建的BST树具有唯一性:以 i为根节点,其左子树由[1,i-1] 构成,右子树由[i+1,n] 构成。
此时进行如下分析:
- 如果数组是空的,也就是只能有一种的情况,所以 得到
f(0)=1
; - 如果数组的元素只有一个时候,就是单节点情况,
f(1)=1
; - 如果有两个元素:
1,2
。就会有两种情况,所以可以得到:
f(2)=f(0) * f(1)+ f(1) * f(0)。1是根的情况与2是根的情况。
- 此时若是有三个元素 1,2,3.
f(3) =f(0) ∗ f(2) +f(1) ∗ f(1) +f(2) ∗ f(0)。表示的是为1为根,2为根,3为根的情况。于是我们可以根据以上的推敲得到以下公式:也推敲出可以是动态规划的思想来解决这道题目
代码:
public class Solution {
public int numTrees(int n) {
int [] f=new int[n+1];
f[0]=1;
f[1]=1;
for(int i=2;i<=n;i++){
for(int k=1;k<=i;k++){
f[i]+=f[k-1]*f[i-k];
}
}
return f[n];
}
}
95-Unique Binary Search Trees-II
在完成了上一题的计算以后,我们知道了对于上一题的解放是,对于 1--n
之间的某一个树来说 其左子树就是 1--i-1
; 右子树是i+1--n
其中的i就是那个根。此时时候,将左右两边的相乘即可得到我们的个数,但是现在而言是当我们完成所有的输出思想还是相同的:
对于 i节点来说 我们使用递归来创建其左子树,然后递归创建右子树,最后将其左右子树在以i为根节点的左右子树上合并起来,就是我们要得到的数。
如下图完美诠释了我们的思路与思想。
代码:
import java.util.*;
public class Solution {
public ArrayList<TreeNode> generateTrees(int n) {
return unique(1,n);
}
private ArrayList<TreeNode> unique(int low,int high){
ArrayList<TreeNode> array=new ArrayList<TreeNode>();
// 在不能满足条件时候进行退出
if(low>high){
array.add(null);
return array;
}
for(int i=low;i<=high;i++){
// 构建左右子树
ArrayList<TreeNode> left=unique(low,i-1);
ArrayList<TreeNode> right=unique(i+1,high);
for(int j=0;j<left.size();j++){
for(int k=0;k<right.size();k++){
// 对左右子树进行合并
TreeNode root=new TreeNode(i);
root.left=left.get(j);
root.right=right.get(k);
array.add(root);
}
}
}
return array;
}
}
98-Validate Binary Search Tree
思路: 在解决这道题目的时候想到的是对于任意的一棵树来说都是其左子树要小于根节点,然后右子树大于根节点,前面我们讲过,对于树类型的题目而言,使用栈的收益最高,然后特定的层序是使用队列实现。在特定的时候 例如使用到的循环每个小的部分使用递归也是可以做较好的实现。首先想到的是栈的实现,就是先,中,后序实现,但是我们发现,对于平衡二叉树来说,以上的三种方法遍历完成以后并没有特定的规律。然后使用到层序遍历,发现对于层序遍历来说,由于对于一棵树的左右子树来说,其在层序遍历的时候是完完全全邻接在一起的,这个时候就可以判断两两邻接的是不是递增即可。
然后考虑到的是递归来解决这类的问题,在下面的一个系列三——递归中会进行较为细致的讲解。下面我们来进行两个方法的具体编程。
方法一 中序遍历:
使用中序遍历以后,两两判断其是否是递增的