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

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

题目描述

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

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

BST的本质特性

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

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

完整递归代码实现

/*** 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,整个树不合法
代码中的短路效应:
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定义

  • 误区:认为只需当前节点左右子节点满足条件即可
    // 错误做法:仅比较左右子节点
    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. 优化建议:显式传递上下界(递归法变种)

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

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/pingmian/82334.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MathQ-Verify:数学问题验证的五步流水线,为大模型推理筑牢数据基石

MathQ-Verify&#xff1a;数学问题验证的五步流水线&#xff0c;为大模型推理筑牢数据基石 大语言模型在数学推理领域进展显著&#xff0c;但现有研究多聚焦于生成正确推理路径和答案&#xff0c;却忽视了数学问题本身的有效性。MathQ-Verify&#xff0c;通过五阶段流水线严格…

八股战神-JVM知识速查

1.JVM组成 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; JVM是Java程序的运行环境 组成部分&#xff1a; 类加载器&#xff1a;加载字节码文件到内存 运行时数据区&#xff1a;包括方法区&#xff0c;堆&#xff0c;栈&#xff0c;程序计数器&#xff0c;本地…

Maven:在原了解基础上对pom.xml文件进行详细解读

一、pom.xml文件 就像项目管理软件 Make 的 MakeFile、Ant 的 build.xml 一样&#xff0c;Maven 项目的核心是 pom.xml。POM( Project Object Model&#xff0c;项目对象模型 ) 定义了项目的基本信息&#xff0c;用于描述项目如何构建&#xff0c;声明项目依赖&#xff0c;等等…

Spring Cloud项目登录认证从JWT切换到Redis + UUID Token方案

背景介绍 在传统的Spring Boot项目中&#xff0c;用户登录认证常见的方案是使用JWT&#xff08;JSON Web Token&#xff09;来实现无状态的身份验证。JWT凭借自包含用户信息、方便前后端分离、性能较好等优势被广泛采用。 然而&#xff0c;在实际项目中&#xff0c;JWT也有一…

MongoDB 快速整合 SpringBoot 示例

1.添加依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spr…

Flyweight(享元)设计模式 软考 享元 和 代理属于结构型设计模式

1.目的&#xff1a;运用共享技术有效地支持大量细粒度的对象 Flyweight&#xff08;享元&#xff09;设计模式 是一种结构型设计模式&#xff0c;它的核心目的是通过共享对象来减少内存消耗&#xff0c;特别是在需要大量相似对象的场景中。Flyweight 模式通过将对象的共享细节与…

002大模型-提示词工程,少样本提示,角色扮演,思维链

一、提示词工程 二、少样本提示 三、角色扮演 四、思维链

华为OD机试真题——传递悄悄话(二叉树最长路径问题)(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

「读书报告」Spark实时大数据分析

这本书是清华大学出版社2018年出版的&#xff0c;我是2020年读的&#xff0c;说真的的&#xff0c;不怎么喜欢这本书&#xff0c;所以作者我都不想提。有的人可能会奇怪&#xff0c;ailx10&#xff0c;你一个搞网络安全的&#xff0c;怎么会去读大数据相关的书&#xff0c;哎&a…

2025 河北ICPC( D. 金泰园(二分)-- C.年少的誓约(公式转化))

文章目录 2025 河北ICPCD. 金泰园&#xff08;二分&#xff09;C.年少的誓约(公式转化)总结 2025 河北ICPC 题目链接&#xff1a; Attachments - The 9th Hebei Collegiate Programming Contest - Codeforces sdccpc20250522 - Virtual Judge 赛时&#xff1a;5道 D. 金泰…

QT学习一

对于选择qmake还是cmake&#xff0c;现在写的暂时先用qmake 1.命名规范和快捷键 2.按钮控件常用API //创建第一个按钮QPushButton * btn new QPushButton;//让btn对象 依赖在mywidget窗口中btn->setParent(this);//显示文本btn->setText("第一个按钮");//创建…

【Elasticsearch】给所索引创建多个别名

Elasticsearch 是可以给索引创建多个别名的。 为什么可以创建多个别名 1. 灵活性 - 别名可以为索引提供一个更易于理解的名称&#xff0c;方便用户根据不同的业务场景或用途来引用同一个索引。例如&#xff0c;一个索引可能同时服务于多个不同的应用程序或服务&#xff0c;通…

使用 OpenCV 实现哈哈镜效果

在计算机视觉和图像处理领域&#xff0c;OpenCV 提供了非常强大的图像几何变换能力&#xff0c;不仅可以用于纠正图像&#xff0c;还能制造各种“有趣”的视觉效果。今天&#xff0c;我们就来实现一个经典的“哈哈镜”效果&#xff0c;让图像像在游乐园里一样被拉伸、压缩、扭曲…

AI|Java开发 IntelliJ IDEA中接入本地部署的deepseek方法

目录 连接本地部署的deepseek&#xff1a; IntelliJ IDEA中使用deepseek等AI&#xff1a; 用法一&#xff1a;让AI写代码 用法二&#xff1a;选中这段代码&#xff0c;右键&#xff0c;可以让其解释这段代码的含义。这时显示的解释是英文的。 连接本地部署的deepseek&#…

如何使用两块硬盘作为 Ubuntu24 的系统盘,实现坏掉一块不影响系统运行。

最近我想使用Ubuntu组一个NAS系统&#xff0c;想实现系统盘冗余&#xff0c;各位大佬可以给点建议吗。 Deep Seek 为了实现两块硬盘作为 Ubuntu 24 系统盘的冗余配置&#xff08;RAID 1&#xff09;&#xff0c;确保一块硬盘损坏时系统仍可运行&#xff0c;以下是详细步骤&am…

【2025最新】虚拟机安装macos,VMware在Windows11上安装macOS 15完整图文教程 - 新手也能轻松上手

引言 想体验苹果系统但不想买Mac电脑&#xff1f;别担心&#xff01;本教程将手把手教你如何在Windows11环境下&#xff0c;通过VMware虚拟机安装macOS Sequoia15系统。即使你是零基础小白&#xff0c;按照这个步骤操作&#xff0c;也能轻松搞定&#xff01; 准备工作 在开始…

论文阅读笔记——Emerging Properties in Unified Multimodal Pretraining

BAGEL 论文 商业闭源系统与学术/开源模型的差距很大&#xff0c;BAGEL 旨在通过开源统一架构大规模交错数据主要解决&#xff1a; 架构割裂&#xff1a;理解/生成分属两条网络&#xff0c;信息被压缩在少量条件 token 中&#xff0c;长上下文推理受限。数据贫乏&#xff1a;主…

Go 语言基础1 Slice,map,string

更多个人笔记见&#xff1a; github个人笔记仓库 gitee 个人笔记仓库 个人学习&#xff0c;学习过程中还会不断补充&#xff5e; &#xff08;后续会更新在github上&#xff09; 文章目录 stirng 字符串区分 rune&#xff0c;byte&#xff0c;string字符串操作strings 库相关 f…

C# AI(Trae工具+claude3.5-sonnet) 写前后端

这是一个AI 写的前后端分离项目,通过AI编程&#xff0c;开发电商管理系统&#xff08;登陆、注册&#xff09; 使用的AI工具为 Trae工具(字节国际版)claude3.5-sonnet(目前代码最强模型) 前端为 vue3Bootstrap 后端为 C# net5.0(因为我电脑里面已经安装了这个新版更好) do…

10G/25G PCS only mode for CoaXPress Over Fiber

背景 在CoaXPress Over Fiber的需求中, 需要利用XGMII的PCS 实现25G 数据速率的稳定传输&#xff0c;也就是不需要其MAC层&#xff0c;只保留PMA PCS层&#xff0c;借用其物理端口 线缆&#xff0c;实现其它协议的数据传输。 25G PCS 25GMII 的 TX/RX 时钟频率在 DDR&#xff…