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

hot100 二叉搜索树中第 K 小的元素(230)

本题采用中序遍历递归剪枝算法(又称“计数状态递减拦截法”)解决二叉搜索树第 k 小元素的检索问题。其核心本质是将二叉搜索树在中序遍历下的全局有序性转化为自左向右、自底向上的计数状态推进,利用计数器归零实现对目标数值的精准捕获与即时剪枝。当前提供的源码实现了在时间复杂度 O(h + k)(其中 h 为树的高度)和额外空间复杂度 O(h) 条件下的高效定位,最终走向是精准返回树中第 k 小的节点数值。

一、 问题本质与数据模型

对于给定的二叉搜索树(BST),其核心物理特性是:中序遍历(左 -> 根 -> 右)访问节点的顺序,完美对应了节点数值从低到高排列的严格递增序列。

如果将查找第 k 小元素的问题看作是对有序序列的索引定位,最朴素的策略是遍历全树并将所有节点存储到线性容器中,但这会产生不必要的空间开销。

为了破除空间冗余的困局,算法引入了“计数递减与即时拦截模型”。通过维护一个全局可变计数器 k,在标准的递归中序遍历中,每当完成对左子树的探测回溯到当前节点时,意味着发现了一个更小的元素,计数器 k 相应执行减 1 操作。一旦计数器 k 缩减至 0,说明当前访问的节点即为目标节点,此时将其数值锁定并写入结果变量。同时,后续的所有子树递归操作均会被提前拦截,达成无意义计算的彻底物理消除。

二、 算法演进对比

在查找二叉搜索树第 k 小元素的场景中,带剪枝的中序遍历算法在运行效率与内存控制上实现了高度优化:

解法名称时间复杂度空间复杂度核心原理物理瓶颈 / 缺陷
全序收集法O(n)O(n)完整中序遍历全树并将结果存入数组,直接通过下标索引 k - 1 定位内存消耗与节点规模成正比,无法处理超大规模数据或流式数据
带剪枝的中序遍历(当前解法)O(h + k)O(h)利用 BST 中序递增特性,计数器归零即时拦截返回并终止后续遍历受制于树的拓扑结构,若树极度倾斜,调用栈深度退化为 O(n)
迭代显式栈法O(h + k)O(h)使用外部栈模拟中序遍历,弹出第 k 个元素时直接跳出循环需要手动维护栈结构,代码结构相对繁琐

三、 核心分支控制逻辑与决策证明

当前源码的控制流完全依赖于dfs(root)函数内的递归压栈、全局计数器k的步进更新以及k <= 0的边界拦截,其内部决策分支证明如下:

1. 退出与剪枝拦截分支:if (root == null || k <= 0)

  • 执行:直接返回(终止当前分支探测)。

  • 物理意义:包含两种拦截场景。若是root == null,说明触及虚拟空边界,属于常规触底回溯;若是k <= 0,说明目标元素已经在之前的遍历路径中被成功捕获并锁定了答案,后续所有的左、右子树调用均为多余的算力浪费,必须强制物理拦截。

2. 严格保序左探测:dfs(root.left);

  • 执行:优先向左子树深处压栈推进。

  • 数学证明:根据 BST 定义,左子树所有节点均严格小于当前根节点。要寻找从小到大排列的第 k 个元素,必须确保更小的值被优先计数,因此左探测必须作为第一顺位执行。

3. 计数状态更新与命中判定:k--; if (k == 0) { ans = root.val; }

  • 执行:计数器自减,并核验是否归零。

  • 数学证明:中序遍历在执行完dfs(root.left)回溯到当前根节点时,表明左子树中所有更小的元素已经完成了计步。当前根节点正是当前未访问元素中的最小值。将其计入步长(k--)后,若k == 0,则在数学上完全等价于“当前节点是全局第 k 小的元素”,触发结果捕获。

4. 右探测延伸:dfs(root.right);

  • 执行:向当前节点的右子树推进。

  • 数学证明:右子树节点值均严格大于当前根节点。当左子树及当前根节点处理完毕且计数器仍大于 0 时,说明第 k 小的元素必然位于值域更大的右半区,因此需要将搜索弹道转向右子树。

四、 算法执行状态机步进示例

以输入二叉搜索树root = [3, 1, 4, null, 2],k = 1为例(规模 n = 4),展示计数器递减与提前剪枝的状态机上演进过程:

步骤当前访问节点值全局计数器 k 的状态值触发的分支与状态判定空间调用栈物理状态说明
初始313 不为空且 k 大于 0,触发左递归栈深: [3]
1111 不为空且 k 大于 0,触发左递归栈深: [3, 1]
2null (1的左孩子)1触发 root == null,直接返回栈深: [3, 1, null] -> 弹出
311 -> 0执行 k-- 变为 0,判定 k == 0 成立,锁定 ans = 1栈深: [3, 1]
410触发右递归,传入 1 的右孩子 2栈深: [3, 1]
520满足 k <= 0 拦截条件,不执行任何子树访问,直接返回栈深: [3, 1, 2] -> 弹出
6101 的右递归返回,1 节点处理完毕栈深: [3, 1] -> 弹出
730回溯到根节点 3,执行 k-- 使得 k 变为 -1栈深: [3]
83-1触发右递归,传入 3 的右孩子 4栈深: [3]
94-1满足 k <= 0 拦截条件,不执行任何子树访问,直接返回栈深: [3, 4] -> 弹出
103-13 的右递归返回,全局构建与搜索完毕栈深: [3] -> 弹出,全盘流程终止

五、 源码实现

/** * 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 { public int kthSmallest(TreeNode root, int k) { // 初始化全局计数器,将其绑定至类成员变量空间 this.k = k; // 启动深度优先搜索中序遍历 dfs(root); // 返回在遍历过程中被精准截获的物理答案 return ans; } private int ans; private int k; private void dfs(TreeNode root) { // 核心控制与剪枝边界:若节点为空,或者计数器已归零或转负(说明答案已被捕获),直接拦截返回 if (root == null || k <= 0) { return; } // 步骤 1:保序下推,优先检索值域更小的左子树 dfs(root.left); // 步骤 2:状态计步,回溯至当前节点,说明发现一个有效小值,计数器自减 k--; // 状态判定:若计数器恰好归零,说明当前节点即为全局第 k 小的目标元素 if (k == 0) { ans = root.val; } // 步骤 3:弹道转向,在当前值及左子树均无法填满 k 的步长时,深入右子树检索 dfs(root.right); } }

六、 复杂度分析

1. 时间复杂度:O(h + k)

  • 分析:中序遍历从根节点出发,首先需要沿最左侧路径一路下推到树中的绝对最小值节点,此步的代价取决于树的高度 h。随后,算法依序访问从小到大的前 k 个节点。由于引入了k <= 0的即时剪枝拦截,在访问完第 k 个节点并锁定答案后,后续所有的节点访问和遍历都会被常数阶操作拦截直接返回。

  • 结论:时间复杂度为 O(h + k)。在完全平衡二叉树中,高度 h 为对数阶,总时间接近 O(log n + k);在最坏情况下(树退化为单链表),h 可达 n。

2. 空间复杂度:O(h)

  • 分析:算法在运行中,外部没有申请多余的集合对象,其空间占用完全依赖于底层维护的深度优先搜索递归方法调用栈。递归栈的最大深度与二叉树的高度 h 保持严格的同步关系。

  • 结论:最大额外空间开销表现为 O(h),空间消耗的本质是拓扑树高的函数。

http://www.cnnetsun.cn/news/3291767.html

相关文章:

  • 鸿蒙新特性:@ohos.vibrator 触感反馈引擎实战
  • Claude Code + Opus 4.7:面向工程落地的自适应AI编程工作流
  • AD5593R与TM4C129ENCPDT硬件设计与驱动开发指南
  • 短剧出海译制速度实测:5款AI工具最快多久译完一部剧?
  • Unity后处理实战:三步打造电影级画面与性能优化指南
  • ClaudeCode接入的两种技术路径:本地直连与平台中转深度对比
  • Source Han Serif CN:免费开源中文字体完全指南
  • 5分钟掌握!FanControl风扇控制软件完整中文使用指南
  • TMC7300与PIC18F46K42的有刷直流电机驱动方案解析
  • [Dify实战] 客户回访记录怎么自动整理成摘要、风险点和跟进任务?
  • ZFX山海证券外汇:把外汇领域风控思路做到位——框架分析与提示整理
  • 工业信号采集抗干扰方案:FOD4216与dsPIC30F实战
  • 从 PHP 到 AI + Golang,程序员自救转型手记(三十一):增加 config 模型、数据表自动迁移优化
  • 图神经网络过平滑问题:从谱域分析到4层GCN的收敛证明
  • TradingAgents-CN:基于多智能体LLM的智能投资分析平台终极指南
  • 2026最新8款企业级AI编程工具实测深度对比
  • 线结构光标定精度对比:灰度重心法 vs Steger算法 vs 互相关法,3种中心线提取方案实测
  • 如何高效使用GBFR-Logs:3步解决《碧蓝幻想:Relink》DPS统计难题的完整指南
  • 不用多设备模拟器跑通 ShareKit 碰一碰分享
  • 企业级跨平台设备管理终极解决方案:Fleet开源平台深度实战指南
  • GPT-5.6 贴脸开大!价格直接腰斩,Claude 突然不香了?国内也直接用
  • 学 Simulink——基于双二阶广义积分器(DSOGI)的单相并网锁相环仿真
  • 【收藏干货】AI大模型应用开发到底是什么?未来10年,程序员必掌握的核心新能力(2026版)
  • Figma中文界面汉化插件:3分钟解决设计师的语言困扰
  • Forza-Mods-AIO:重新定义极限竞速地平线游戏的革命性修改工具
  • MediaCrawler数据采集实战:3个步骤快速掌握小红书抖音爬虫
  • 高精度ADC ADS127L11与PIC18F55K42数据采集系统设计
  • Video2X:让AI重塑你的视频记忆,480p秒变4K的魔法工具
  • hostha配置详解:管理网、存储网、业务网三网监控最佳实践
  • 零编程经验10分钟开发Chrome插件:用GPT-4实现浏览器行为即时编译