98. 验证二叉搜索树

148 阅读2分钟

二叉搜索树的定义

  • 它或者是一棵空树,或者是具有下列性质的二叉树:
  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值

方法一:直接递归

时间复杂度:O(n),其中 n 为二叉树的节点个数。在递归调用的时候二叉树的每个节点最多被访问一次,因此时间复杂度为 O(n)。

思路:

设计一个递归函数 helper(root, lower, upper) 来递归判断,函数表示考虑以 root 为根的子树,判断子树中所有节点的值是否都在 (l,r) 的范围内(注意是开区间)。如果 root 节点的值 val 不在 (l,r)的范围内说明不满足条件直接返回,否则我们要继续递归调用检查它的左右子树是否满足,如果都满足才说明这是一棵二叉搜索树。

那么根据二叉搜索树的性质,在递归调用左子树时,我们需要把上界 upper 改为 root.val,即调用 helper(root.left, lower, root.val),因为左子树里所有节点的值均小于它的根节点的值。同理递归调用右子树时,我们需要把下界 lower 改为 root.val,即调用 helper(root.right, root.val, upper)

函数递归调用的入口为 helper(root, -inf, +inf)inf 表示一个无穷大的值。

class Solution {
    public boolean isValidBST(TreeNode root) {
        return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }
         
    public boolean dfs(TreeNode root, long min, long max) {
        if (root == null) {
            return true;
        }
        if (root.val >= max || root.val <= min) {
            return false;
        }
        return dfs(root.left, min, root.val) && dfs(root.right, root.val, max);
    }
}

方法二:利用中序遍历

先中序遍历二叉树,把结果存在list中,再扫一遍list,两两比较大小判断。BST的中序遍历结果一定是升序的。

迭代版

class Solution {
    public boolean isValidBST(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        //迭代中序遍历
        while (node != null || !stack.isEmpty()) {
            if (node != null) {
                stack.push(node);
                node = node.left;
            } else {
                TreeNode father = stack.pop();
                list.add(father.val);
                node = father.right;
            }
        }
        //两两比较大小
        for (int i = 0; i < list.size() - 1; i++) {
            if(list.get(i) >= list.get(i + 1)) {
                return false;
            }
        }
        return true;
    }
}

递归版

class Solution {
    List<Integer> list = new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
        traverse(root);
        if (list.size() <= 1) {//有0个或者1个节点,true
            return true;
        }
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i) <= list.get(i - 1)) {
                return false;
            }
        }
        return true;
    }
    //递归中序遍历二叉树
    public void traverse(TreeNode root) {
        if (root == null) {
            return;
        }
        traverse(root.left);
        list.add(root.val);
        traverse(root.right);
    }
}

方法三:优化版中序遍历

先递归判断左子树,再判断当前节点,在判断右子树。

class Solution {
    TreeNode max = null;//记录当前最大节点,如果记录数值的话可能会越界
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        //访问左子树,递归
        if (!isValidBST(root.left)) {
            return false;
        }
        //访问中间节点
        if (max != null && root.val <= max.val) {//注意max!=null,叶子节点
            return false;
        } else {
            max = root;
        }
        //访问右子树,递归
        return isValidBST(root.right);
    }
}