98. 验证二叉搜索树 🟨
题目
98. 验证二叉搜索树 中等
题目
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 严格小于 当前节点的数。
- 节点的右子树只包含 严格大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true

示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

陷阱
二叉搜索树(左节点 < 根节点 < 右节点),提笔就写出这样的错误逻辑:
js
// ❌ 典型的错误想法
if (node.left && node.left.val >= node.val) return false;
if (node.right && node.right.val <= node.val) return false;
// 为什么错? 请看下面这棵树:
5
/ \
4 6
/ \
3 7
所以,验证 BST 的核心不仅是比较左右孩子,而是上下界限(Bounds) 的约束。
题解
方法:深度优先 DFS
解题思路
每一个节点在被访问时,不仅要知道自己是谁,还要知道自己允许的取值范围(min, max)。
- 根节点:无拘无束,范围是 (-Infinity, Infinity)。
- 向左走:最大值被限制住了。左孩子的值必须严格小于当前节点的值。
- 向右走:最小值被限制住了。右孩子的值必须严格大于当前节点的值。
只要在往下走的过程中,有任何一个节点的值越界了,整棵树就是非法的。
复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(n)
js
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val
this.left = left
this.right = right
}
}
function isValidBST(root) {
const validate = (node, min, max) => {
console.log('♻️ node', node ? node.val : null, 'min', min, 'max', max)
// 空节点默认是合法的 BST
if (node === null) return true
// 如果当前节点的值越界了,直接判死刑
// 注意:BST 中不能有相等的值,所以是 <= 和 >=
if (node.val <= min || node.val >= max) {
return false
}
// 递归检查左子树,最大界限更新为当前节点的值
const isLeftValid = validate(node.left, min, node.val)
// 递归检查右子树,最小界限更新为当前节点的值
const isRightValid = validate(node.right, node.val, max)
return isLeftValid && isRightValid
}
return validate(root, -Infinity, Infinity)
}
const tree = new TreeNode(4)
tree.left = new TreeNode(2, new TreeNode(1), new TreeNode(3))
tree.right = new TreeNode(5, null, new TreeNode(6))
console.log('🌰', JSON.stringify(isValidBST(tree)))
console.log('------')
const tree2 = new TreeNode(5)
tree2.left = new TreeNode(1)
tree2.right = new TreeNode(4, new TreeNode(3), new TreeNode(6))
console.log('🌰', JSON.stringify(isValidBST(tree2)))
执行结果
♻️ node 4 min -Infinity max Infinity
♻️ node 2 min -Infinity max 4
♻️ node 1 min -Infinity max 2
♻️ node null min -Infinity max 1
♻️ node null min 1 max 2
♻️ node 3 min 2 max 4
♻️ node null min 2 max 3
♻️ node null min 3 max 4
♻️ node 5 min 4 max Infinity
♻️ node null min 4 max 5
♻️ node 6 min 5 max Infinity
♻️ node null min 5 max 6
♻️ node null min 6 max Infinity
🌰 true
------
♻️ node 5 min -Infinity max Infinity
♻️ node 1 min -Infinity max 5
♻️ node null min -Infinity max 1
♻️ node null min 1 max 5
♻️ node 4 min 5 max Infinity
🌰 false
方法:中序遍历
- 时间复杂度:O(n)
- 空间复杂度:O(n)
js
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val
this.left = left
this.right = right
}
}
function isValidBST(root) {
let stack = []
let inorder = -Infinity
while (stack.length || root !== null) {
console.log(
'♻️ stack',
stack.map(node => node.val),
'inorder',
inorder,
)
while (root !== null) {
stack.push(root)
root = root.left
}
console.log(
'stack',
stack.map(node => node.val),
)
root = stack.pop()
// 如果中序遍历得到的节点的值小于等于前一个 inorder,说明不是二叉搜索树
if (root.val <= inorder) {
console.log('❌', root.val, inorder)
return false
}
inorder = root.val
root = root.right
}
return true
}
const tree = new TreeNode(4)
tree.left = new TreeNode(2, new TreeNode(1), new TreeNode(3))
tree.right = new TreeNode(5, null, new TreeNode(6))
console.log('🌰', JSON.stringify(isValidBST(tree)))
console.log('------')
const tree2 = new TreeNode(5)
tree2.left = new TreeNode(1)
tree2.right = new TreeNode(4, new TreeNode(3), new TreeNode(6))
console.log('🌰', JSON.stringify(isValidBST(tree2)))
执行结果
♻️ stack [] inorder -Infinity
stack [ 4, 2, 1 ]
♻️ stack [ 4, 2 ] inorder 1
stack [ 4, 2 ]
♻️ stack [ 4 ] inorder 2
stack [ 4, 3 ]
♻️ stack [ 4 ] inorder 3
stack [ 4 ]
♻️ stack [] inorder 4
stack [ 5 ]
♻️ stack [] inorder 5
stack [ 6 ]
🌰 true
------
♻️ stack [] inorder -Infinity
stack [ 5, 1 ]
♻️ stack [ 5 ] inorder 1
stack [ 5 ]
♻️ stack [] inorder 5
stack [ 4, 3 ]
❌ 3 5
🌰 false