leetcode98.验证二叉搜索树:递归法中序遍历的递增性验证之道

一、题目深度解析与BST核心性质

题目描述

验证二叉搜索树(BST)是算法中的经典问题,要求判断给定的二叉树是否满足BST的定义:

  1. 左子树中所有节点的值严格小于根节点的值
  2. 右子树中所有节点的值严格大于根节点的值
  3. 左右子树本身也必须是二叉搜索树

BST的本质特性

  • 中序遍历性质 :BST的中序遍历结果是一个严格递增的序列 。例如:

    复制代码
        3
       / \
      1   5
       \   \
        2   6
    中序遍历结果:[1, 2, 3, 5, 6](严格递增)
  • 递归定义:每个节点需满足左子树最大值 < 当前节点值 < 右子树最小值,但直接递归验证需传递上下界,而通过中序遍历的递增性可更简洁地验证。

二、递归解法的核心实现与逻辑框架

完整递归代码实现

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    TreeNode max; // 记录中序遍历的前一个节点(初始为null)

    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true; // 空树是有效的BST
        }
        
        // 1. 递归验证左子树
        boolean leftValid = isValidBST(root.left);
        if (!leftValid) {
            return false; // 左子树不合法,直接返回false
        }
        
        // 2. 验证当前节点与前一个节点的关系(中序遍历的递增性)
        if (max != null && max.val >= root.val) {
            return false; // 当前节点值不大于前一个节点值,违反BST定义
        }
        max = root; // 更新前一个节点为当前节点(中序遍历顺序:左-中-右)
        
        // 3. 递归验证右子树
        boolean rightValid = isValidBST(root.right);
        return rightValid; // 右子树的合法性决定最终结果
    }
}

核心设计解析:

  1. 全局变量max

    • 作用:记录中序遍历过程中访问的前一个节点(初始为null)
    • 更新时机:在处理完左子树后、处理右子树前,访问当前节点时更新
    • 核心逻辑:确保当前节点值 > 前一个节点值(中序遍历递增性)
  2. 递归顺序

    • 左子树 → 当前节点 → 右子树(中序遍历顺序)
    • 先递归处理左子树,再检查当前节点,最后处理右子树
    • 保证在检查当前节点时,左子树已完全处理,max是左子树的最后一个节点(即当前节点的前驱)

三、核心问题解析:递归顺序与返回值条件

1. 递归顺序的本质:中序遍历的递归实现

递归步骤拆解:
  1. 递归左子树isValidBST(root.left)

    • 确保左子树本身是BST,且左子树的所有节点已按中序遍历处理完毕
  2. 处理当前节点

    • 检查当前节点值是否大于前一个节点值(max.val < root.val
    • 更新max为当前节点,作为后续右子树的前驱节点
  3. 递归右子树isValidBST(root.right)

    • 右子树的所有节点值必须大于当前节点值(由中序遍历递增性保证)
中序遍历映射:
复制代码
递归顺序:左子树 → 当前节点 → 右子树
对应中序遍历:左子树节点 < 当前节点 < 右子树节点

2. 返回值条件的逻辑闭环

条件判断链:
  • 左子树不合法:直接返回false,无需继续检查
  • 当前节点不满足递增性:返回false,右子树无需检查
  • 右子树不合法:返回false,整个树不合法
代码中的短路效应:
java 复制代码
if (!leftValid) return false; // 左子树失败则整体失败
if (max != null && max.val >= root.val) return false; // 当前节点失败则整体失败
return rightValid; // 右子树决定最终结果

四、递归流程深度模拟:以无效BST为例

示例无效BST结构:

复制代码
    5
   / \
  4   6
     / \
    3   7

问题:左子节点4 >= 根节点5?不,实际问题在右子树3 < 根节点6

递归验证过程:

  1. 处理根节点5
    • 递归左子树4(叶子节点,返回true)
    • 检查max=null,更新max=4
    • 递归右子树6:
      • 递归左子树3(叶子节点,返回true)
      • 检查max=4 < 3?不,4 >= 3,返回false
    • 根节点5的右子树验证失败,整体返回false

关键失败点:

  • 右子树的左节点3值为3,前一个节点是根节点5的左子树节点4,4 >= 3,违反递增性

五、算法复杂度分析

1. 时间复杂度

  • O(n):每个节点仅访问一次,n为树的节点数
  • 递归过程中每个节点执行常数级操作,总时间线性于节点数

2. 空间复杂度

  • O(h) :h为树的高度(递归栈深度)
    • 平衡BST:h=logn,空间复杂度O(logn)
    • 最坏情况(退化为链表):h=n,空间复杂度O(n)

3. 与迭代法对比

方法 优势 劣势
递归法 代码简洁,中序遍历自然递归实现 深树可能导致栈溢出
迭代法 避免栈溢出,空间更可控 栈操作逻辑较复杂

六、核心技术点总结:递归验证的三大关键

1. 中序遍历的递归映射

  • 顺序保证:递归顺序天然符合中序遍历的左-中-右顺序
  • 状态传递 :通过全局变量max传递中序遍历的前驱节点,避免显式传递上下界

2. 递增性的核心判断

  • 单一条件:无需检查复杂的上下界,只需保证当前节点 > 前驱节点
  • 数学等价:中序遍历递增性等价于BST的严格定义,简化判断逻辑

3. 递归终止条件的设计

  • 空树处理:直接返回true,作为递归终止的安全边界
  • 叶子节点:左右子树为空时,仅需检查自身与前驱节点的关系

七、常见误区与优化建议

1. 错误理解BST定义

  • 误区 :认为只需当前节点左右子节点满足条件即可

    java 复制代码
    // 错误做法:仅比较左右子节点
    if (root.left != null && root.left.val >= root.val) return false;
    if (root.right != null && root.right.val <= root.val) return false;
  • 正确逻辑:需保证左子树所有节点 < 当前节点,而非仅左子节点

2. 忽略全局变量的作用

  • 误区:未使用前驱节点记录,导致无法验证跨层节点关系
  • 正确设计max记录中序前驱,确保整个序列递增

3. 优化建议:显式传递上下界(递归法变种)

java 复制代码
public boolean isValidBST(TreeNode root) {
    return validate(root, null, null);
}

private boolean validate(TreeNode node, Integer lower, Integer upper) {
    if (node == null) return true;
    int val = node.val;
    // 当前节点需满足 lower < val < upper
    if (lower != null && val <= lower) return false;
    if (upper != null && val >= upper) return false;
    // 左子树最大值 < val,右子树最小值 > val
    return validate(node.left, lower, val) && validate(node.right, val, upper);
}
  • 优势:显式传递上下界,逻辑更严谨,避免依赖全局变量
  • 原理:每个节点的合法值范围由父节点决定,左子树值 < 当前节点值,右子树值 > 当前节点值

八、总结:递归法验证BST的本质是中序递增性的递归实现

本算法通过递归实现中序遍历,利用BST的中序递增性简化验证逻辑,核心在于:

  1. 递归顺序的选择:左-中-右的递归顺序天然对应中序遍历,确保前驱节点的正确性
  2. 状态的隐式传递 :通过全局变量max记录前驱节点,避免复杂的参数传递
  3. 条件的短路效应:左子树或当前节点验证失败时立即返回,提高效率

理解这种递归解法的关键是将BST的复杂定义转化为中序遍历的简单递增性判断。递归法的简洁性使其成为验证BST的经典解法,尤其适合树深度较小的场景。在实际工程中,需根据树的规模选择递归或迭代实现,平衡代码简洁性与稳定性。

相关推荐
oioihoii7 分钟前
C++23 新成员函数与字符串类型的改动
算法·c++23
似水এ᭄往昔29 分钟前
【数据结构】——二叉树堆(下)
数据结构·算法
bloglin9999933 分钟前
java的vscode扩展插件
java·开发语言·vscode
GG不是gg34 分钟前
Prim算法剖析与py/cpp/java语言实现
算法
Magnum Lehar1 小时前
vulkan游戏引擎的pipeline管道实现
java·开发语言·游戏引擎
Magnum Lehar1 小时前
vulkan游戏引擎的vulkan/shaders下的image实现
java·前端·游戏引擎
秋山落叶万岭花开ღ1 小时前
探索数据结构之顺序表:从入门到精通
数据结构·python·算法
shangjg31 小时前
Java网络编程性能优化
java·网络·性能优化
会敲键盘的猕猴桃很大胆1 小时前
Redis实战-缓存篇(万字总结)
java·数据库·spring boot·redis·缓存