news 2026/4/23 14:39:42

从零开始刷算法——二叉树篇:验证二叉搜索树 + 二叉树中第k小的元素

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零开始刷算法——二叉树篇:验证二叉搜索树 + 二叉树中第k小的元素

二叉搜索树(Binary Search Tree, BST)是数据结构中的“秩序守护者”。它的核心定义非常简单:对于任意节点,其左子树所有节点的值 < 当前节点 < 右子树所有节点的值。

这个看似简单的定义,在实际算法落地时衍生出了两个最经典的方向:

  1. 约束(Constraint):如何验证一棵树是否严格遵守了规则?

  2. 顺序(Order):如何利用这个规则快速找到特定的元素?

本文将通过两道经典题目,结合代码实现,深入剖析 BST 的递归哲学。


一、 验证的艺术:自顶向下的区间约束

验证一棵二叉搜索树,最直观的错误想法是:只判断“当前节点”和“左右孩子”的大小关系。这是不够的,因为 BST 要求的是整个左子树都要小于根节点,整个右子树都要大于根节点。

因此,我们需要一种**自顶向下(Top-Down)**的思维:父节点要把自己的“数值范围约束”传递给子节点。

1. 核心代码解析

我们使用递归函数,为每个节点维护一个开区间(left, right)

  • 根节点的范围是(-∞, +∞)

  • 走时:最大值被更新为当前节点的值(不能超过爸爸)。

  • 走时:最小值被更新为当前节点的值(不能小于爸爸)。

C++代码实现:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { // 思路: 递归判断左右子树是不是符合我们二叉搜索树的要求 public: // 使用 long long 是为了防止节点值为 INT_MIN 或 INT_MAX 时造成的边界判断错误 bool isValidBST(TreeNode* root, long long left = LLONG_MIN, long long right = LLONG_MAX) { if (root == nullptr) return true; // 核心逻辑: // 1. 当前值必须在 (left, right) 区间内 // 2. 递归左子树:上界变为 root->val // 3. 递归右子树:下界变为 root->val return root->val > left && root->val < right && isValidBST(root->left, left, root->val) && isValidBST(root->right, root->val, right); } };

2. 深度思考:为什么要用long long

代码中leftright的默认值使用了LLONG_MINLLONG_MAX。这是因为 LeetCode 的测试用例中,树节点的值可能正好是int类型的最小值或最大值。 如果使用INT_MIN作为初始下界,当根节点就是INT_MIN时,判断条件root->val > left会变成INT_MIN > INT_MIN(False),导致误判。提升数据类型范围是解决此类边界问题的最佳手段。

3. 时空复杂度分析

  • 时间复杂度:O(N)

    • 我们需要遍历树中的每一个节点来确认其有效性。每个节点只会被访问一次。

  • 空间复杂度:O(H)

    • H 为树的高度。主要消耗在于递归调用栈。

    • 最坏情况(链状树)为 O(N),平均情况(平衡树)为 O(log N)。


二、 顺序的艺术:二叉搜索树中第 K 小的元素

如果说上一题是验证规则,这一题就是利用规则。 BST 的最大特性在于:如果对其进行中序遍历(Inorder Traversal),得到的序列是严格单调递增的。

正如代码思路中所写:“想象把这一棵二叉搜索树拍扁”,它就是一个有序数组[1, 2, 3, 4...]。我们要找第 K 小的数,其实就是中序遍历过程中的第 K 个节点。

1. 核心代码解析

这里不需要构造整个数组,那样太浪费空间。我们利用DFS(深度优先搜索)配合引用传递的计数器k,实现“边走边数,数到即停”。

C++代码实现:

/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), * right(right) {} * }; */ class Solution { // 思路: 想象把这一棵二叉搜索树拍扁, 它刚好会是1234的顺序呈现 // 因此我们优先考虑左边也就是dfs // left,left找完然后找right,这里用&k来维护全局第k小 int ans; // 注意这里的 k 是引用传递 (int& k),这是实现“全局计数”的关键 void dfs(TreeNode* root, int& k) { if (root == nullptr) return; // 1. 先去最左边(最小的地方) dfs(root->left, k); // 2. 中序位置:处理当前节点 // 每经过一个节点,k 减 1,表示这就“划掉”了一个比目标小的数 if (--k == 0) { ans = root->val; return; // 找到了,直接返回,不再继续深搜 } // 3. 左边和自己都不是,那就去右边找 dfs(root->right, k); } public: int kthSmallest(TreeNode* root, int k) { dfs(root, k); return ans; } };

2. 深度思考:引用传递int& k的妙用

很多初学者容易在这里写成值传递int k

  • 值传递:每层递归里的k都是副本,左子树里把k减到了 0,回到父节点时k还是原来的值,导致计数失效。

  • 引用传递int& k使得所有递归层级共享同一个变量。它就像一个倒计时器,无论递归走到哪里,只要遍历了一个节点,这个全局计数器就减 1。

此外,if (--k == 0)后的return虽然不能直接跳出整个递归栈(C++没有直接中断机制),但它能有效阻止当前分支继续向右递归,起到一定的剪枝优化作用。

3. 时空复杂度分析

  • 时间复杂度:O(H + k)

    • 我们需要先深入到最左下角(高度 H),然后开始回溯并计数 k 次。

    • 一旦找到第 k 个数,我们就不再处理剩余的节点了。

    • 在最坏情况下(k = N),复杂度为 O(N)。

  • 空间复杂度:O(H)

    • 主要消耗为递归栈的空间。

    • 最坏情况 O(N),平均情况 O(log N)。


三、 总结

这两道题目完美诠释了 BST 的两面性:

  1. IsValidBST:侧重于**“区间”**。利用递归参数携带状态(最大最小值),自顶向下进行“封锁”检查。

  2. KthSmallest:侧重于**“顺序”**。利用中序遍历的特性,配合全局计数器,自底向上进行“枚举”查找。

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

SSM心理健康系统84459(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面

系统程序文件列表系统项目功能&#xff1a;用户,咨询师,文章类型,心理文章,在线咨询,在线预约,心理档案,用户评价,心理课程SSM心理健康系统开题报告一、课题研究背景与意义&#xff08;一&#xff09;研究背景在社会竞争日益激烈的当下&#xff0c;各类人群的心理健康问题愈发凸…

作者头像 李华
网站建设 2026/4/23 13:56:55

TensorFlow模型实战:5分钟云端部署,比本地快10倍仅1块钱

TensorFlow模型实战&#xff1a;5分钟云端部署&#xff0c;比本地快10倍仅1块钱 你是不是也遇到过这种情况&#xff1f;创业团队刚做出一个AI想法&#xff0c;想快速验证TensorFlow模型效果&#xff0c;结果发现大家都是MacBook办公——没有NVIDIA显卡&#xff0c;根本跑不动G…

作者头像 李华
网站建设 2026/4/23 12:32:23

DeepSeek-R1代码验证优化:云端GPU+自动执行器省时50%

DeepSeek-R1代码验证优化&#xff1a;云端GPU自动执行器省时50% 你是不是也遇到过这样的情况&#xff1f;作为编程教练&#xff0c;每天要批改几十份学生作业&#xff0c;每一份都要手动运行、比对输出、检查逻辑错误。更头疼的是&#xff0c;你还得用 DeepSeek-R1 生成参考代…

作者头像 李华
网站建设 2026/4/23 12:32:38

HeyGem无障碍应用:视障人士语音视频制作教程

HeyGem无障碍应用&#xff1a;视障人士语音视频制作教程 你有没有想过&#xff0c;一段原本需要“看”的视频内容&#xff0c;也能被“听”得清清楚楚、生动有趣&#xff1f;对于视障群体来说&#xff0c;这不仅是便利&#xff0c;更是一种平等获取信息的权利。而今天我们要聊…

作者头像 李华
网站建设 2026/4/23 12:31:12

PyTorch-2.x部署问题汇总:常见报错及解决方案大全

PyTorch-2.x部署问题汇总&#xff1a;常见报错及解决方案大全 1. 引言 随着PyTorch 2.x系列的广泛采用&#xff0c;其在编译优化、性能提升和API统一等方面带来了显著改进。然而&#xff0c;在实际部署过程中&#xff0c;尤其是在基于官方底包构建的定制化环境中&#xff08;…

作者头像 李华