「前端刷题」98. 验证二叉搜索树

213 阅读2分钟

「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。

题目

链接:leetcode-cn.com/problems/va…

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

**输入:**root = [2,1,3] **输出:**true

示例 2:

**输入:**root = [5,1,4,null,null,3,6] **输出:**false **解释:**根节点的值是 5 ,但是右子节点的值是 4 。

提示:

  • 树中节点数目范围在[1, 104]
  • -231 <= Node.val <= 231 - 1

思路

递归遍历

分析

递归遍历,所有节点满足,二叉树的左孩子 < 根, 右孩子 > 根

代码

var isValidBST = function(root) {

    function dfs(root){
        if (root == null ){
            return true
        }
        if (root.left){
            if (root.left.val >= root.val) return false 
        }
        if (root.right){
            if (root.right.val <= root.val) return false
        }

        return dfs(root.left) && dfs(root.right)
    }

    return dfs(root)
};

但是这样也不一定正确,如下面的case就不通过

[10,5,15,null,null,6,20] 返回true,期望false

实际上BST,需要整个右子树都大于根,整个左子树都小于根

前面已经判断了,右孩子一定大于根,左孩子一定小于根;

需要再附加条件:任意节点的值必须大于其左子树的最右节点;同时小于右子树的最左节点。从根节点开始检查,一旦发现不满足则返回false。

整理代码

var isValidBST = function(root) {

    function dfs(root){
        if (root == null ){
            return true
        }
        if (root.left){
            if (root.left.val >= root.val) return false 
            let rightest = getRightest(root.left)
            if (rightest && root.val <= rightest.val) return false

        }
        if (root.right){
            if (root.right.val <= root.val) return false
            let leftest = getLeftest(root.right)
            if (leftest && root.val >= leftest.val) return false
        }

        return dfs(root.left) && dfs(root.right)
    }

    function getRightest(node){
        while (node && node.right) node = node.right
        return node
    }

    function getLeftest(node){
        while (node && node.left) node = node.left
        return node
    }

    return dfs(root)
};

中序遍历

var isValidBST = function(root) {
    var queue = []
    function dfs(root){
        if (!root) return
        if (root.left) dfs(root.left)
        if (root) queue.push(root.val)
        if (root.right) dfs(root.right)
    }

    dfs(root)
    
    for (let i =0; i< queue.length-1; i++){
        if (queue[i] >= queue[i+1]) return false
    }
    
    return true
};

其他思路

二叉搜索树的性质就是左子树中所有节点的值都小于(这里是<而非<=)根节点,右子树中所有节点的值都大于(同理这里是>而非>=)根节点 直接递归,验证左子树的时候,将左子树值的最小范围和最大范围作为参数传入,同理右子树也是如此 细节点:初学者做这题很容易有误区:BST 不是左小右大么,那我只要检查 root.val > root.left.val 且 root.val < root.right.val 不就行了?这样是不对的,因为 BST 左小右大的特性是指 root.val 要比左子树的所有节点都更大,要比右子树的所有节点都小,你只检查左右两个子节点当然是不够的。

// 限定以 root 为根的子树节点必须满足 max.val > root.val > min.val
var isValidBST = function (root, min = -Infinity, max = Infinity) {
  // 如果是空节点
  if (!root) return true;
  // 当前节点的值大于最小值,小于最大值;(换句话说,当前节点的值大于左子树所有节点的值,小于右子树中所有节点的值 )
  // 限定左子树的最大值是 root.val,右子树的最小值是 root.val
  return (
    root.val > min &&
    root.val < max &&
    isValidBST(root.left, min, root.val) &&
    isValidBST(root.right, root.val, max)
  );
};