news 2026/7/11 19:29:38

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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),空间消耗的本质是拓扑树高的函数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/11 19:27:25

鸿蒙新特性:@ohos.vibrator 触感反馈引擎实战

前言 触感反馈&#xff08;Haptic Feedback&#xff09;是现代移动设备交互体验的重要组成部分。从键盘按键的轻微振动到闹钟的持续提醒&#xff0c;从游戏中的碰撞反馈到通知到达时的短震&#xff0c;线性马达驱动的高品质振动已经成为区分设备档次的关键指标。 HarmonyOS 通过…

作者头像 李华
网站建设 2026/7/11 19:27:11

Claude Code + Opus 4.7:面向工程落地的自适应AI编程工作流

1. 项目概述&#xff1a;为什么说Claude Code Opus 4.7是当前代码协作场景下最值得投入的组合&#xff1f;最近两周&#xff0c;我几乎没碰过其他AI编程工具——不是因为它们不好&#xff0c;而是Claude Code搭配刚发布的Opus 4.7&#xff0c;确实把“理解意图—拆解逻辑—生成…

作者头像 李华
网站建设 2026/7/11 19:27:01

AD5593R与TM4C129ENCPDT硬件设计与驱动开发指南

1. AD5593R与TM4C129ENCPDT的硬件特性解析AD5593R是一款8通道、12位精度的多功能数据转换器&#xff0c;每个通道都可以独立配置为ADC输入、DAC输出、数字输入或数字输出模式。这种灵活的I/O配置能力使其成为嵌入式系统原型开发的理想选择。芯片内部集成了基准电压源&#xff0…

作者头像 李华
网站建设 2026/7/11 19:26:54

短剧出海译制速度实测:5款AI工具最快多久译完一部剧?

人工翻译一部100集短剧&#xff0c;通常需要两三周&#xff1b;AI能做到多快&#xff1f;我们实测了5款AI工具的处理速度&#xff0c;对比人工翻译的周期数据。一、速度测试说明测试标准&#xff1a;以100集、每集25分钟&#xff08;总计2500分钟&#xff09;的标准短剧为测试场…

作者头像 李华
网站建设 2026/7/11 19:25:59

Unity后处理实战:三步打造电影级画面与性能优化指南

1. 项目概述&#xff1a;从“能看”到“好看”的视觉革命在Unity开发中&#xff0c;我们常常会遇到一个瓶颈&#xff1a;模型、光照、材质都做得不错&#xff0c;但最终画面总感觉“差一口气”&#xff0c;缺乏电影、3A大作那种扑面而来的质感与氛围。这种差距&#xff0c;很大…

作者头像 李华
网站建设 2026/7/11 19:25:55

ClaudeCode接入的两种技术路径:本地直连与平台中转深度对比

1. 项目概述&#xff1a;这不是简单的“连上就行”&#xff0c;而是选对路子少踩半年坑 “ClaudeCode接入”这六个字&#xff0c;最近在技术圈里出现的频率&#xff0c;已经快赶上当年大家折腾GitHub Copilot初期那会儿了。但和Copilot那种开箱即用不同&#xff0c;ClaudeCode目…

作者头像 李华