[路飞]_合法二叉搜索树

136 阅读4分钟

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

面试题 04.05. 合法二叉搜索树

题目介绍

实现一个函数,检查一棵二叉树是否为二叉搜索树。

示例1

输入: 
    2
   / \
  1   3
输出: true

示例2

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4

解题思路

首先在讲解思路之前,需要说明一下什么是二叉搜索树:
如果左子树非空,那么左子树上的结点的值全都小于根节点的值,如果右子树非空,那么右子树上的结点的值全都大于根节点的值,并且其左右子树均为二叉搜索树

思路一:递归

递归法需要注意的是 左子树的全部结点 的值都小于根节点,而不是小于当前结点的值,右子树同理,举个例子

1643879196(1).png
对于图中的结点 6,如果是二叉搜索树,那么其不止应该小于结点 15,同时应该大于结点 10

所以用递归解题的时候,需要将当前节点的可取值范围进行传递

解题步骤

  1. 如果当前结点为空,返回 true
  2. 如果当前结点的值不在当前结点的可取值范围内,说明不是二叉搜索树,返回 false
  3. 递归遍历左右子树,左子树的取值范围是 (最小值, 当前结点的值),右子树的取值范围是 (当前结点的值, 最大值)

合法二叉搜索树.gif

解题代码

var isValidBST = function(root) {
    // 根节点的最大小值设置为 无穷大 和 无穷小
    return _isValidBST(root, +Infinity, -Infinity)
};

var _isValidBST = function(root, max, min) {
    if (!root) return true
    // 如果当前结点的取值范围不在最大小值范围内,那么说明不是二叉搜索树
    if (root.val >= max || root.val <= min) return false
    // 分别递归判断左右子树,左子树的取值范围为(当前结点的最小值到当前结点的值),右子树的取值范围为(当前结点的值到当前结点的最大值)
    return _isValidBST(root.left, root.val, min) && _isValidBST(root.right, max, root.val)
}

思路二:中序遍历

由中序遍历的性质我们可以知道,如果将一棵二叉搜索树进行中序遍历,那么最后会得到一个递增的数组,该数组中每一个结点的值都小于上一个结点的值

解题步骤

  1. 如果当前结点为空,返回 true
  2. 定义数组 nodeArr 存放树的结点,定义 prev 用于保存上一个结点的值
  3. 将当前结点及其左子节点依次插入数组 nodeArr 中,直到左子节点不存在
  4. 从数组 nodeArr 中弹出一个值
  5. 如果该值小于 prev 节点的值,说明不是二叉搜索树,返回 false
  6. prev 保存当前结点,root 指向当前结点的右子节点

合法二叉搜索树(1).gif

解题代码

var isValidBST = function(root) {
    if (!root) return true
    const nodeArr = []
    let prev
    while (nodeArr.length || root) {
        // 如果 root 存在,则将 root 节点插入到 nodeArr 中,然后继续判断左子节点
        while (root) {
            nodeArr.push(root)
            root = root.left
        }
        // 从 nodeArr 弹出一个节点进行比较
        root = nodeArr.pop()
        // 如果节点的值小于 prev 节点的值,说明不是二叉搜索树
        if (prev && root.val <= prev.val) return false
        // prev 保存当前节点
        prev = root
        // 继续比较当前节点的右节点
        root = root.right
    }
    // 如果能够走到这一步,说明是二叉搜索树
    return true
};

思路三:中序遍历递归

这种方法结合了 中序遍历递归 的方法

解题步骤

  1. 定义全局变量 prev 保存上一个节点
  2. 递归判断左子树是否是二叉搜索树
  3. 如果当前节点的值小于等于 prev 节点的值,说明不是二叉搜索树
  4. 递归判断右子树是否是二叉搜索树
  5. 走到这一步说明是二叉搜索树

解题代码

let prev
var isValidBST = function(root) {
     if (!root) return true
     if (!isValidBST(root.left)) return false
     if (prev && root.val <= prev.val) return false
     prev = root
     if (!isValidBST(root.right)) return false
     return true
};

局限性
这种方法定义了全局变量,所有在 leetcode 中提交会报错,但是单个例子运行是没问题的

1643881757.png