「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」
题目介绍
实现一个函数,检查一棵二叉树是否为二叉搜索树。
示例1
输入:
2
/ \
1 3
输出: true
示例2
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
解题思路
首先在讲解思路之前,需要说明一下什么是二叉搜索树:
如果左子树非空,那么左子树上的结点的值全都小于根节点的值,如果右子树非空,那么右子树上的结点的值全都大于根节点的值,并且其左右子树均为二叉搜索树
思路一:递归
递归法需要注意的是 左子树的全部结点 的值都小于根节点,而不是小于当前结点的值,右子树同理,举个例子
对于图中的结点 6,如果是二叉搜索树,那么其不止应该小于结点 15,同时应该大于结点 10
所以用递归解题的时候,需要将当前节点的可取值范围进行传递
解题步骤
- 如果当前结点为空,返回
true - 如果当前结点的值不在当前结点的可取值范围内,说明不是二叉搜索树,返回
false - 递归遍历左右子树,左子树的取值范围是 (最小值, 当前结点的值),右子树的取值范围是 (当前结点的值, 最大值)
解题代码
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)
}
思路二:中序遍历
由中序遍历的性质我们可以知道,如果将一棵二叉搜索树进行中序遍历,那么最后会得到一个递增的数组,该数组中每一个结点的值都小于上一个结点的值
解题步骤
- 如果当前结点为空,返回
true - 定义数组
nodeArr存放树的结点,定义prev用于保存上一个结点的值 - 将当前结点及其左子节点依次插入数组
nodeArr中,直到左子节点不存在 - 从数组
nodeArr中弹出一个值 - 如果该值小于
prev节点的值,说明不是二叉搜索树,返回false - 用
prev保存当前结点,root指向当前结点的右子节点
解题代码
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
};
思路三:中序遍历递归
这种方法结合了 中序遍历 与 递归 的方法
解题步骤
- 定义全局变量
prev保存上一个节点 - 递归判断左子树是否是二叉搜索树
- 如果当前节点的值小于等于
prev节点的值,说明不是二叉搜索树 - 递归判断右子树是否是二叉搜索树
- 走到这一步说明是二叉搜索树
解题代码
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 中提交会报错,但是单个例子运行是没问题的