news 2026/6/15 22:06:57

从零开始刷算法——二叉树篇:验证二叉搜索树 + 二叉树中第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/6/15 12:41:43

终极解决方案:如何让PS手柄在PC游戏中大放异彩?

终极解决方案&#xff1a;如何让PS手柄在PC游戏中大放异彩&#xff1f; 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows DS4Windows作为一款免费开源的控制器映射工具&#xff0c;彻底解决…

作者头像 李华
网站建设 2026/6/15 19:29:46

如何高效实现多语言翻译?试试HY-MT1.5-7B大模型镜像

如何高效实现多语言翻译&#xff1f;试试HY-MT1.5-7B大模型镜像 在全球化日益深入的今天&#xff0c;跨语言沟通早已超越简单的文本转换&#xff0c;成为科研协作、企业出海、内容本地化等关键环节的核心支撑。然而&#xff0c;传统翻译方案往往面临质量与效率难以兼顾、数据隐…

作者头像 李华
网站建设 2026/6/15 13:11:08

用户创作分享社区:发布你的DDColor修复作品平台推荐

用户创作分享社区&#xff1a;发布你的DDColor修复作品平台推荐 1. 引言 随着人工智能技术的发展&#xff0c;图像修复与上色已成为数字内容创作中的重要一环。尤其是对于历史影像、家庭老照片等黑白素材&#xff0c;如何通过智能化手段实现高质量的色彩还原&#xff0c;成为…

作者头像 李华
网站建设 2026/6/15 15:35:16

4款高效镜像工具测评:Qwen2.5免配置部署体验

4款高效镜像工具测评&#xff1a;Qwen2.5免配置部署体验 1. 引言&#xff1a;大模型部署的效率革命 随着大语言模型&#xff08;LLM&#xff09;在实际业务中的广泛应用&#xff0c;如何快速、稳定地将模型部署到生产环境成为开发者关注的核心问题。传统部署方式往往涉及复杂的…

作者头像 李华
网站建设 2026/6/15 12:27:08

AMD Ryzen处理器终极调优指南:5分钟掌握SDT调试工具完整实战

AMD Ryzen处理器终极调优指南&#xff1a;5分钟掌握SDT调试工具完整实战 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: http…

作者头像 李华
网站建设 2026/6/15 13:14:47

Kotaemon员工培训:智能手册助力新人快速上岗

Kotaemon员工培训&#xff1a;智能手册助力新人快速上岗 1. 背景与目标 在现代企业中&#xff0c;新员工的入职培训往往面临信息分散、学习路径不清晰、上手周期长等问题。传统文档式培训材料缺乏交互性&#xff0c;难以满足快速迭代的技术岗位需求。为解决这一痛点&#xff…

作者头像 李华