题目难度: 中等

原题链接

今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

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

示例 1:

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

示例 2:

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

题目思考

  1. 可以利用二叉搜索树的哪些性质来判断?

解决方案

方案 1

思路

  • 二叉搜索树最直观的性质就是: 当前节点的值大于左子树上任意节点的值, 并小于右子树上任意节点的值
  • 所以我们可以利用这一性质, 引入当前树的上下界来递归判断
  • 如果当前根节点的值在上下界范围内, 则递归判断左右子树, 同时更新左子树的上界和右子树的下界为当前节点的值; 否则说明不是合法的二叉搜索树, 返回 false
  • 另外注意初始化时上下界是-inf 和+inf, 表示整个树的根节点可以取任何值

复杂度

  • 时间复杂度 O(N): 每个节点只需要遍历一遍
  • 空间复杂度 O(logN): 递归栈的消耗, 递归深度最多是树的高度, 也即 logN

代码

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        # 方法1: 加入上下界递归判断
        def check(node, mn, mx):
            # mn和mx表示当前子树的上下界取值范围, 即子树内的所有节点的值都要在(mn, mx)之间
            if not node:
                return True
            if not mn < node.val < mx:
                # 当前节点不属于该范围内, 说明不是合法的二叉搜索树
                return False
            # 检查左右子树, 注意上下界也要更新
            # 即左子树的mx和右子树的mn要更新为当前节点值
            return check(node.left, mn, node.val) & check(
                node.right, node.val, mx)

        # 初始化的上下界自然是-inf和+inf
        return check(root, -float('inf'), float('inf'))

方案 2

思路

  • 二叉搜索树还有另一个性质, 即中序遍历是有序的
  • 所以我们也可以利用这一性质, 引入额外参数 pre 来代表前一个节点的值, 并引入一个 valid 布尔值表示是否合法
  • 然后中序遍历并比较 pre 和当前节点 cur 的值: 如果 pre 大于等于它的话则说明不是合法的二叉搜索树, 将 valid 置为 false; 否则更新 pre 为当前节点的值, 用于之后的判断, 并继续中序遍历
  • 另外注意初始化时 pre 是-inf, 表示整个树的最小节点可以取任何值

复杂度

  • 时间复杂度 O(N): 每个节点只需要遍历一遍
  • 空间复杂度 O(logN): 递归栈的消耗, 递归深度最多是树的高度, 也即 logN

代码

class Solution:
    def isValidBST(self, root: TreeNode) -> bool:
        # 方法2: 中序遍历, 引入额外参数pre表示上一个元素的值
        pre = -float('inf')
        valid = True

        def inorder(node):
            nonlocal pre
            nonlocal valid
            if not node or not valid:
                # 如果节点为空或者已经不是合法二叉搜索树了, 直接退出
                return
            inorder(node.left)
            if pre >= node.val:
                # 前一个值大于等于当前值, 不满足二叉搜索树的要求, 无效
                valid = False
                return
            # 不要忘了更新前一个值为当前值, 用于下一次判断
            pre = node.val
            inorder(node.right)

        inorder(root)
        return valid

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我