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

今日算法(二叉树剪枝)

题目描述

给你二叉搜索树的根节点root,同时给定最小边界low和最大边界high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。

  • 修剪树不应该改变保留在树中的元素的相对结构(即如果没有被移除,原有的父子代关系都应当保留)
  • 可以证明存在唯一的答案
  • 结果应当返回修剪好的二叉搜索树的新的根节点(根节点可能会根据给定的边界发生改变)

核心思路:利用二叉搜索树的性质递归修剪

二叉搜索树(BST)的核心性质:

  • 左子树所有节点值 < 根节点值
  • 右子树所有节点值 > 根节点值
  • 中序遍历结果为升序序列

基于这个性质,我们可以用递归的方式对每个节点进行判断和修剪:

  1. 如果当前节点为空,直接返回空(递归终止条件)
  2. 如果当前节点值 <low:说明当前节点和其左子树都不符合条件,直接返回右子树的修剪结果
  3. 如果当前节点值 >high:说明当前节点和其右子树都不符合条件,直接返回左子树的修剪结果
  4. 如果当前节点值在[low, high]区间内:递归修剪其左、右子树,最后返回当前节点

完整代码实现(C++)

cpp

/** * 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: TreeNode* trimBST(TreeNode* root, int low, int high) { // 1. 递归终止条件:节点为空,直接返回 if (!root) return nullptr; // 2. 当前节点值小于low:左子树全部不符合条件,只需要修剪右子树 if (root->val < low) { return trimBST(root->right, low, high); } // 3. 当前节点值大于high:右子树全部不符合条件,只需要修剪左子树 if (root->val > high) { return trimBST(root->left, low, high); } // 4. 当前节点值在区间内:递归修剪左右子树,保留当前节点 root->left = trimBST(root->left, low, high); root->right = trimBST(root->right, low, high); return root; } };

详细执行流程解析

我们以示例 1 为例,模拟递归执行过程:输入:root = [1,0,2], low = 1, high = 2

  1. 处理根节点1

    • 1[1,2]区间内,需要修剪其左右子树
    • 先递归处理左子树0,再递归处理右子树2
  2. 处理左子树节点0

    • 0 < 1,不符合条件,直接返回其右子树(nullptr
    • 根节点的左指针最终指向nullptr
  3. 处理右子树节点2

    • 2[1,2]区间内,修剪其左右子树(均为nullptr,无变化)
    • 返回节点2
  4. 最终结果

    • 根节点1保留,左子树为空,右子树为2,即[1,null,2]

关键细节与易错点

  1. 递归终止条件的位置必须先判断节点是否为空,否则访问root->val会导致空指针异常。

  2. 节点值越界的处理逻辑

    • 当节点值< low时,只需要递归处理右子树(左子树所有值更小,必然越界)
    • 当节点值> high时,只需要递归处理左子树(右子树所有值更大,必然越界)这是利用 BST 性质的核心优化,避免了不必要的递归调用。
  3. 根节点的变化当原根节点值不在区间内时,新的根节点会由其左 / 右子树的修剪结果决定,这也是题目中 “根节点可能改变” 的原因。


复杂度分析

  • 时间复杂度:\(O(n)\),其中 n 为树的节点数。每个节点最多被访问一次。
  • 空间复杂度:\(O(h)\),其中 h 为树的高度。递归调用栈的深度等于树的高度,最坏情况下(树退化为链表)为 \(O(n)\)。

拓展:迭代版实现(可选)

如果不想使用递归,也可以用迭代的方式实现,分为两步:

  1. 先找到新的根节点(值在[low, high]内的第一个节点)
  2. 分别修剪新根节点的左、右子树

cpp

class Solution { public: TreeNode* trimBST(TreeNode* root, int low, int high) { if (!root) return nullptr; // 1. 找到新的根节点 while (root && (root->val < low || root->val > high)) { if (root->val < low) root = root->right; else root = root->left; } if (!root) return nullptr; // 2. 修剪左子树 TreeNode* cur = root; while (cur->left) { if (cur->left->val < low) { cur->left = cur->left->right; } else { cur = cur->left; } } // 3. 修剪右子树 cur = root; while (cur->right) { if (cur->right->val > high) { cur->right = cur->right->left; } else { cur = cur->right; } } return root; } };

总结

修剪二叉搜索树的核心就是利用 BST 的有序性,通过递归快速定位需要保留的节点,整个过程不需要额外空间,仅通过修改指针就能完成修剪,同时保证了原有的父子关系不变。

http://www.jsqmd.com/news/854231/

相关文章:

  • 别再只会用plot画图了!用Matlab ode45求解微分方程时,这3种可视化技巧让结果更清晰
  • HTTPS单向认证、双向认证、抓包原理与反抓包策略详解
  • Simulink中VSG转子运动方程实现详解
  • 如何用Python自动化脚本提升大麦网抢票成功率:完整配置指南
  • 中山优才教育反洗钱ARMS报名怎么样?授权、报名条件、费用、流程 - 优选机构推荐
  • 等效电路模型驱动的车辆横向稳定性建模方法【附程序】
  • 2026专业医疗建筑设计公司推荐:破解复杂场景痛点 筑就安全医疗空间 - 资讯速览
  • OpenMMLab环境配置避坑指南:从CUDA 11.6到PyTorch 1.13,如何为MMRotate 0.3.4找到对的mmcv-full?
  • [深度研究] 超越个体智能:多智能体系统综述 —— L.I.F.E. 四把钥匙
  • 【计算机组成原理】无符号整数乘法原理(基于移位累加,零基础看懂CPU乘法)
  • 嵌入式Linux内核调试实战:多核死锁与内存问题诊断
  • 西部数据开源RISC-V技术栈:SweRV Core 2.0、OmniXtend与验证框架解析
  • 时间序列自监督学习避坑指南:从SimCLR到MAE,三大流派怎么选?
  • 2026虾火锅底料批发权威指南:高性价比供应商测评推荐 - 资讯速览
  • 从玩家到创造者:用BepInEx开启游戏模组开发之旅
  • 订阅制养不活AI:一场关于“固定收入VS浮动成本”的错配游戏
  • 从‘玄学’到‘科学’:我是如何系统化搞定Amesim和Simulink联合仿真的(环境变量/编译器深度解析)
  • ESP8266通过MQTT 3.1.1协议连接阿里云物联网平台实战指南
  • 敏捷开发在研发团队中的实践知识详解
  • 如何快速解锁教学控制:JiYuTrainer极域电子教室防控制完全指南
  • 别再手动拉黑发件人了!用Python+深度学习模型,5步搞定智能垃圾邮件过滤器
  • 虾火锅底料批发常见问题解答(2026最新专家版) - 资讯速览
  • 以太网口电路PCB设计实战:从原理到布局布线的完整指南
  • Nmap - Zenmap GUI工具
  • 花五分钟在NAS上搭了个Code-Server,结果成了我出场率最高的开发环境
  • 【GaussDB】GaussDB 常见问题及解决方案汇总
  • Meta与牛津联手发布VGGT-Ω:用2000万视频喂出的「3D重建巨无霸」!
  • 树状数组 - P2184 贪婪大陆
  • 收藏干货:MySQL/PG/人大金仓/达梦语法差异对照表
  • 你正在找靠谱企业用车平台?这几个维度比榜单靠谱 - 资讯速览