当前位置: 首页 > news >正文

【LeetCode热题100道笔记】验证二叉搜索树

题目描述

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 严格小于 当前节点的数。
节点的右子树只包含 严格大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

示例 1:
在这里插入图片描述
输入:root = [2,1,3]
输出:true

示例 2:
在这里插入图片描述
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

提示:
树中节点数目范围在[1,104][1, 10^4][1,104]
−231<=Node.val<=231−1-2^31 <= Node.val <= 2^31 - 1231<=Node.val<=2311

思考一:前序遍历(递归检测值范围)

二叉搜索树的根节点的值大于所有左子树的节点,小于所有右子树的节点。写递归函数时很容易把节点值的范围搞错,如下图不是二叉搜索树:
在这里插入图片描述
节点41 小于祖先节点42,不满足二叉搜索树根节点右子树所有节点大于根节点值条件。
因此要定义一个递归函数,设置上下限参数,检测左子树时才更新上限值,检测右子树时才更新下限值。
核心是为每个节点设定合法值区间:根节点的合法区间为 (-∞, +∞),左子节点的区间上限更新为父节点值(需严格小于父节点),右子节点的区间下限更新为父节点值(需严格大于父节点),递归验证所有节点是否在合法区间内。

算法过程

  1. 初始化递归:调用辅助函数 check,传入根节点及初始区间 (-Infinity, Infinity)(根节点无上下限约束)。
  2. 递归终止条件:若当前节点为 null(空树/空子树),符合BST规则,返回 true
  3. 节点值合法性检测
    • 若当前节点值 <= 区间下限>= 区间上限(违反“左小右大”规则),返回 false
  4. 递归验证子树
    • 验证左子树:左子树的合法区间为 (原下限, 当前节点值)(左子树需严格小于当前节点);
    • 验证右子树:右子树的合法区间为 (当前节点值, 原上限)(右子树需严格大于当前节点);
    • 只有左右子树均验证通过,才返回 true
  5. 返回结果:递归回溯后,返回最终验证结果。

时空复杂度

  • 时间复杂度:O(n),n为二叉树节点总数。
    原因:每个节点仅被验证1次,递归操作无重复遍历,总操作次数与节点数线性相关。
  • 空间复杂度:O(h),h为二叉树高度。
    原因:递归调用栈深度取决于树高,平衡树h=O(log n),链状树h=O(n)。

代码(前序遍历版)

/*** Definition for a binary tree node.* function TreeNode(val, left, right) {*     this.val = (val===undefined ? 0 : val)*     this.left = (left===undefined ? null : left)*     this.right = (right===undefined ? null : right)* }*/
/*** @param {TreeNode} root* @return {boolean}*/
var isValidBST = function(root) {// 初始区间:根节点无上下限,用正负无穷表示return check(root, -Infinity, Infinity);
};// 辅助函数:验证节点是否在 [low, high) 区间内(左闭右开,保证严格大小)
function check(node, low, high) {// 空节点符合BST规则if (!node) return true;// 节点值超出合法区间,不是BSTif (node.val <= low || node.val >= high) {return false;}// 左子树区间更新为 [low, node.val),右子树更新为 [node.val, high)return check(node.left, low, node.val) && check(node.right, node.val, high);
}

思考二:中序遍历(验证序列严格递增)

核心是利用BST的中序遍历特性:BST的中序遍历结果是严格递增的序列(左→根→右,值依次增大)。通过中序遍历收集节点值,再检查序列是否严格递增,即可验证是否为有效BST。

算法过程

  1. 中序遍历收集节点值
    • 初始化空数组 arr,用于存储中序遍历的节点值;
    • 递归执行中序遍历:先遍历左子树,再将当前节点值加入 arr,最后遍历右子树(左→根→右)。
  2. 验证序列严格递增
    • 遍历数组 arr,若存在任意 i 满足 arr[i] >= arr[i+1](非严格递增),返回 false
    • 若所有相邻元素均满足 arr[i] < arr[i+1],返回 true
  3. 返回结果:返回序列验证结果。

时空复杂度

  • 时间复杂度:O(n),n为二叉树节点总数。
    原因:中序遍历收集节点值需O(n),遍历数组验证需O(n),总时间为O(n)。
  • 空间复杂度:O(n)(含存储数组)。
    原因:数组 arr 需存储所有节点值(O(n)),递归调用栈需O(h),总空间由数组主导,为O(n)。
    (优化:可不用数组,用变量记录前一个节点值,空间复杂度降至O(h),见下方补充)

代码(中序遍历版,基础版)

/*** Definition for a binary tree node.* function TreeNode(val, left, right) {*     this.val = (val===undefined ? 0 : val)*     this.left = (left===undefined ? null : left)*     this.right = (right===undefined ? null : right)* }*/
/*** @param {TreeNode} root* @return {boolean}*/
var isValidBST = function(root) {const arr = [];// 中序遍历收集节点值inOrder(root, arr);// 验证序列是否严格递增for (let i = 0; i < arr.length - 1; i++) {if (arr[i] >= arr[i + 1]) return false;}    return true;
};// 中序遍历:左→根→右
function inOrder(node, arr) {if (!node) return;inOrder(node.left, arr);arr.push(node.val);inOrder(node.right, arr);
}
中序遍历优化版(无数组,O(h)空间)
var isValidBST = function(root) {let prev = -Infinity; // 记录前一个节点值,初始为负无穷let isValid = true;   // 标记是否为有效BSTfunction inOrder(node) {if (!node || !isValid) return; // 提前终止(已判定无效)inOrder(node.left);            // 左// 验证当前节点值是否大于前一个节点值if (node.val <= prev) {isValid = false;return;}prev = node.val;               // 更新前一个节点值(根)inOrder(node.right);           // 右}inOrder(root);return isValid;
};

两种方法对比

方法核心逻辑时间复杂度空间复杂度(基础版)优势
前序遍历(值范围)递归验证节点合法区间O(n)O(h)无额外存储,空间更优
中序遍历(有序性)验证中序序列严格递增O(n)O(n)(数组)逻辑直观,易理解
中序遍历(优化版)实时对比前节点值O(n)O(h)兼顾直观与空间效率

适用场景

  • 若追求空间效率,优先选择“前序遍历(值范围)”或“中序遍历优化版”;
  • 若追求代码简洁直观,可选择“中序遍历(数组版)”(节点数较少时无明显性能问题)。

文章转载自:

http://f2GWQkNG.djmdk.cn
http://TG6YUcgO.djmdk.cn
http://4uKdKm61.djmdk.cn
http://pbIl3CsC.djmdk.cn
http://bo9RBS3q.djmdk.cn
http://jilmCKR1.djmdk.cn
http://EbwfQhJX.djmdk.cn
http://xbTTUYUQ.djmdk.cn
http://FXIMfjiB.djmdk.cn
http://sEf3uqNp.djmdk.cn
http://GUtuWMiN.djmdk.cn
http://YrLGFbWY.djmdk.cn
http://AIdZt9Lf.djmdk.cn
http://9PukFH1p.djmdk.cn
http://iwFKQHKY.djmdk.cn
http://GtALpJD3.djmdk.cn
http://eh38BLF1.djmdk.cn
http://qhxXimwZ.djmdk.cn
http://RmT3ApB4.djmdk.cn
http://kYr2eAZ1.djmdk.cn
http://XzI4uVDA.djmdk.cn
http://1o1QEGtQ.djmdk.cn
http://5y6PeaeP.djmdk.cn
http://KH7ow5NR.djmdk.cn
http://QtdqhEla.djmdk.cn
http://F0VTO7N9.djmdk.cn
http://HCVhfpuG.djmdk.cn
http://3dMWMPET.djmdk.cn
http://jGPrwbsa.djmdk.cn
http://HLtQ9qm2.djmdk.cn
http://www.dtcms.com/a/370897.html

相关文章:

  • 垃圾收集器分类
  • AQS原理
  • Nestjs框架: 使用 CASL 库实现基于角色的权限控制(RBAC)与细粒度访问控制的实战演示
  • 计算机主板上的那颗纽扣电池的作用是什么?
  • 【Java实战㉗】Java日志框架实战:Logback与Log4j2的深度探索
  • 【关于线程的一些总结】
  • PyQt5 入门(上):开启 GUI 编程之旅
  • 本体论中的公理与规则——从经典逻辑到神经符号融合的演进
  • linux 内核 - 内核设计原则
  • Vue3中SCSS的使用指南
  • 音转文模型对比FunASR与Faster_whisper
  • 【YOLOv11】3.Pycharm配置
  • 常用配置文件
  • MySQL运维补充
  • JVM中如何调优新生代和老生代?
  • Transformer 架构的演进与未来方向(RNN → Self-Attention → Mamba)——李宏毅大模型2025第四讲笔记
  • 企业级监控方案对比:Zabbix vs Prometheus
  • 【Kubernetes】知识点总结6
  • 力扣3495. 使数组元素都变为零的最少操作次数 详解
  • 新能源研发,用新型实验记录本:ELN
  • 【LeetCode热题100道笔记】将有序数组转换为二叉搜索树
  • 【LeetCode热题100道笔记】二叉树的直径
  • 2023年ASOC SCI2区TOP,改进元启发式算法+考虑医护人员技能水平的家庭健康护理路径规划,深度解析+性能实测
  • wpf之TextBlock
  • Docker安装Ubuntu搭建Android SDK编译环境
  • Golang中逃逸现象, 变量“何时栈?何时堆?”
  • 我用Claude Code 开发了一个浏览器插件
  • LRU 算法和 LFU 算法有什么区别?
  • Cursor安装使用 与 Cursor网页端登录成功,客户端怎么也登陆不上
  • vue + ant-design-vue + vuedraggable 实现可视化表单设计器