树与图中等3 种解法
#98验证二叉搜索树
判断一棵二叉树是否满足所有左子树值严格小于根、右子树值严格大于根。
#二叉搜索树#深度优先搜索#中序遍历
原题
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
- 节点的左子树只包含 严格小于 当前节点的数。
- 节点的右子树只包含 严格大于 当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3] 输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
- 树中节点数目范围在
[1, 104]内 -231 <= Node.val <= 231 - 1
解题主线
- 上下界法把祖先约束一路传给子树,不能只比较父子节点。
- 二叉搜索树的中序遍历必须严格递增。
解法 1:递归上下界
为每个节点维护来自所有祖先的开区间 (lower, upper)。
-
时间复杂度: O(n)
-
空间复杂度: O(h)
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
return validate(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean validate(TreeNode node, long lower, long upper) {
// 空子树天然满足当前祖先传下来的取值约束
if (node == null) return true;
// 开区间保证 BST 中不能出现与边界相等的值
if (node.val <= lower || node.val >= upper) return false;
// 当前值分别收紧左子树上界和右子树下界
return validate(node.left, lower, node.val)
&& validate(node.right, node.val, upper);
}
}解法 2:递归中序遍历
中序访问时与前一个节点值比较,用局部数组承载跨递归帧状态。
-
时间复杂度: O(n)
-
空间复杂度: O(h)
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
return inorder(root, new long[] {Long.MIN_VALUE});
}
private boolean inorder(TreeNode node, long[] previous) {
if (node == null) return true;
// 左子树若已破坏递增性,可立即剪枝
if (!inorder(node.left, previous)) return false;
// 中序序列必须严格递增,相等值同样非法
if (node.val <= previous[0]) return false;
// 更新跨递归帧共享的前驱值后再检查右子树
previous[0] = node.val;
return inorder(node.right, previous);
}
}解法 3:迭代中序遍历
显式栈模拟中序遍历,逐个检查输出序列是否严格递增。
-
时间复杂度: O(n)
-
空间复杂度: O(h)
import java.util.*;
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int val) {
this.val = val;
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode current = root;
// long 哨兵覆盖可能出现的 Integer.MIN_VALUE 节点
long previous = Long.MIN_VALUE;
while (current != null || !stack.isEmpty()) {
// 持续压入左链,显式模拟递归中序遍历
while (current != null) {
stack.push(current);
current = current.left;
}
current = stack.pop();
// 中序输出必须严格递增,重复值也会失败
if (current.val <= previous) return false;
previous = current.val;
current = current.right;
}
return true;
}
}边界与易错点
- 节点值可能等于 int 边界,上下界应使用 long。
- 递归中序的前驱值不能残留在多次调用之间,应使用方法内局部状态。
整理来源
由旧仓库源码复核、去重并整理;展示代码已按 Java 21 语义修正明显问题。
leetcode/src/main/java/tree/Q98_isValidBST.java