题目难度: 中等
今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
实现一个函数,检查一棵二叉树是否为二叉搜索树。
示例 1:
- 输入:
2 / \ 1 3
- 输出: true
示例 2:
- 输入:
5 / \ 1 4 / \ 3 6
- 输出: false
- 解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
题目思考
- 可以利用二叉搜索树的哪些性质来判断?
解决方案
方案 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
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊