题目描述
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
解法1-递归法
解题思路:根据二叉搜索树的特点我们可以知道,根结点的左子树的所有结点的值都小于根结点的值,即根结点的值是左子树值的上界;根结点的右子树的所有结点的值都大于根结点的值,即根结点的值是左子树值的下界。受此启发,我们可以设计一个函数 h e l p e r ( r o o t , l o w e r , u p p e r ) helper(root,lower,upper) helper(root,lower,upper),来判断当前结点的值是否在 ( l o w e r , u p p e r ) (lower,upper) (lower,upper)的范围内,注意,此处是开区间。如果不满足条件,说明不是二叉搜索树,否则继续递归调用 h e l p e r helper helper函数检查它的左右子树是否满足二叉搜索树的条件。
class Solution {
public boolean helper(TreeNode root, double lower, double upper)
{
if(root != null) //如果结点为空,直接返回true
{
//如果当前结点的值不在(lower,upper)的范围内,则不是二叉搜索树
if(root.val <= lower || root.val >= upper)
return false;
//继续检查左子树,注意此时上界变为当前结点的值
if(!helper(root.left, lower, root.val))
return false;
//继续检查右子树,注意此时下界变为当前结点的值
if(!helper(root.right, root.val, upper))
return false;
//如果当前结点,以及其左右子树都满足条件,返回true
return true;
}
return true;
}
public boolean isValidBST(TreeNode root) {
var inf = Double.MAX_VALUE;
return helper(root, -inf, inf);
}
}
复杂度
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n为二叉树的结点个数。因为在递归过程中二叉树的每个结点最多被访问一次,所以为 O ( n ) O(n) O(n),和递归遍历二叉树的时间复杂度一样。
- 空间复杂度:因为递归用到了栈空间,所需的辅助空间为二叉树的深度,最坏情况下为 O ( n ) O(n) O(n),即二叉树为一条链的情况。
解法2-中序遍历法
解题思路:根据二叉搜索树的特性我们可知,二叉搜索树的中序遍历得到的序列一定是升序的。因此,我们可以对二叉搜索树进行中序遍历,在每次访问根结点时,将其值与之前结点的最大值进行比较,若小于之前结点的最大值,则说明不是二叉搜索树。
class Solution {
public boolean isValidBST(TreeNode root) {
var stack = new Stack<TreeNode>();
double pre = - Double.MAX_VALUE;
TreeNode p = root;
//思路和二叉树的中序遍历一样,仅仅是多了一个根结点的值和之前结点最大值的比较
while(p != null || !stack.empty())
{
if(p != null)
{
stack.push(p);
p = p.left;
}
else
{
p = stack.pop();
//如果当前结点的值不大于之前结点的最大值,则不是二叉搜索树
if(p.val <= pre)
return false;
pre = p.val;
p = p.right;
}
}
return true;
}
}
复杂度
- 时间复杂度: O ( n ) O(n) O(n),因为在每个结点最多被访问一次,所以为 O ( n ) O(n) O(n)。
- 空间复杂度:因为用到了栈空间,所需的辅助空间为二叉树的深度,最坏情况下为 O ( n ) O(n) O(n),即二叉树为一条链的情况。