「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。
题目
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 小于 当前节点的数。
- 节点的右子树只包含 大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
- 树中节点数目范围在
[1, 10^4]内 -2^31 <= Node.val <= 2^31 - 1
思考
这是二叉树相关的一道题目,难度中等。
有效的二叉搜索树,需满足左子树节点的值 < 根节点的值 < 右子树节点的值,我们可以发现,这不正是中序遍历的过程吗?那么,我们可以考虑通过中序遍历解决该问题。即每次遍历二叉树的节点时,把根节点存入栈函数中,并找到最左子树的节点,然后开始判断。如果所有的根节点均需要满足上面的条件,则为有效的二叉搜索树。
此外,我们可以通过递归的方法,借助一个递归函数helper(root, lower, upper),判断子树中所有节点的值是否都在 (lower, upper) 的范围内。
解答
方法一:中序遍历
var isValidBST = function(root) {
let stack = []
let inorder = -Infinity
while (stack.length || root) {
// while循环,找到数值最小的叶子节点,存入各个根节点
while (root) {
stack.push(root)
root = root.left
}
// 删除并返回数组的最后一个元素
root = stack.pop()
// 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
if (root.val <= inorder) {
return false
}
// 更新inorder
inorder = root.val
// 更新root:中序遍历是左根右,所以这里赋值为root.right
root = root.right
}
return true
}
复杂度分析
- 时间复杂度 : O(n),其中 n 为二叉树的节点个数。二叉树的每个节点最多被访问一次。
- 空间复杂度 : O(n),其中 n 为二叉树的节点个数。栈最多存储 n 个节点,因此需要额外的 O(n) 的空间。
方法二:递归
const helper = (root, lower, upper) => {
if (!root) {
return true
}
if (root.val <= lower || root.val >= upper) {
return false
}
return helper(root.left, lower, root.val) && helper(root.right, root.val, upper)
}
var isValidBST = function(root) {
return helper(root, -Infinity, Infinity)
}
复杂度分析
- 时间复杂度 : O(n),其中 n 为二叉树的节点个数。在递归调用的时候二叉树的每个节点最多被访问一次。
- 空间复杂度 : O(n),其中 n 为二叉树的节点个数。