创作

完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>

3天内不再提示

如何修剪二叉搜索树

85xj_TheAlgorit 来源:代码随想录 作者:程序员Carl 2021-10-11 14:16 次阅读

如果不对递归有深刻的理解,本题有点难。单纯移除一个节点那还不够,要修剪!

669. 修剪二叉搜索树

给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。

思路

相信看到这道题目大家都感觉是一道简单题(事实上leetcode上也标明是简单)。

但还真的不简单!

递归法

直接想法就是:递归处理,然后遇到root->val < low || root->val > high的时候直接return NULL,一波修改,赶紧利落。

不难写出如下代码:

classSolution{
public:
TreeNode*trimBST(TreeNode*root,intlow,inthigh){
if(root==nullptr||root->val< low || root->val>high)returnnullptr;
root->left=trimBST(root->left,low,high);
root->right=trimBST(root->right,low,high);
returnroot;
}
};

然而[1, 3]区间在二叉搜索树的中可不是单纯的节点3和左孩子节点0就决定的,还要考虑节点0的右子树

所以以上的代码是不可行的!

从图中可以看出需要重构二叉树,想想是不是本题就有点复杂了。

其实不用重构那么复杂。

在上图中我们发现节点0并不符合区间要求,那么将节点0的右孩子 节点2 直接赋给 节点3的左孩子就可以了(就是把节点0从二叉树中移除)

理解了最关键部分了我们在递归三部曲:

  • 确定递归函数的参数以及返回值

这里我们为什么需要返回值呢?

因为是要遍历整棵树,做修改,其实不需要返回值也可以,我们也可以完成修剪(其实就是从二叉树中移除节点)的操作。

但是有返回值,更方便,可以通过递归函数的返回值来移除节点。

这样的做法在二叉树:搜索树中的插入操作二叉树:搜索树中的删除操作中大家已经了解过了。

代码如下:

TreeNode*trimBST(TreeNode*root,intlow,inthigh)
  • 确定终止条件

修剪的操作并不是在终止条件上进行的,所以就是遇到空节点返回就可以了。

if(root==nullptr)returnnullptr;
  • 确定单层递归的逻辑

如果root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点。

代码如下:

if(root->val< low) {
    TreeNode* right = trimBST(root->right,low,high);//寻找符合区间[low,high]的节点
returnright;
}

如果root(当前节点)的元素大于high的,那么应该递归左子树,并返回左子树符合条件的头结点。

代码如下:

if(root->val>high){
TreeNode*left=trimBST(root->left,low,high);//寻找符合区间[low,high]的节点
returnleft;
}

接下来要将下一层处理完左子树的结果赋给root->left,处理完右子树的结果赋给root->right。

最后返回root节点,代码如下:

root->left=trimBST(root->left,low,high);//root->left接入符合条件的左孩子
root->right=trimBST(root->right,low,high);//root->right接入符合条件的右孩子
returnroot;

此时大家是不是还没发现这多余的节点究竟是如何从二叉树中移除的呢?

在回顾一下上面的代码,针对下图中二叉树的情况:

如下代码相当于把节点0的右孩子(节点2)返回给上一层,

if(root->val< low) {
    TreeNode* right = trimBST(root->right,low,high);//寻找符合区间[low,high]的节点
returnright;
}

然后如下代码相当于用节点3的左孩子 把下一层返回的 节点0的右孩子(节点2) 接住。

root->left=trimBST(root->left,low,high);

此时节点3的右孩子就变成了节点2,将节点0从二叉树中移除了。

最后整体代码如下:

classSolution{
public:
TreeNode*trimBST(TreeNode*root,intlow,inthigh){
if(root==nullptr)returnnullptr;
if(root->val< low) {
            TreeNode* right = trimBST(root->right,low,high);//寻找符合区间[low,high]的节点
returnright;
}
if(root->val>high){
TreeNode*left=trimBST(root->left,low,high);//寻找符合区间[low,high]的节点
returnleft;
}
root->left=trimBST(root->left,low,high);//root->left接入符合条件的左孩子
root->right=trimBST(root->right,low,high);//root->right接入符合条件的右孩子
returnroot;
}
};

精简之后代码如下:

classSolution{
public:
TreeNode*trimBST(TreeNode*root,intlow,inthigh){
if(root==nullptr)returnnullptr;
if(root->val< low) returntrimBST(root->right,low,high);
if(root->val>high)returntrimBST(root->left,low,high);
root->left=trimBST(root->left,low,high);
root->right=trimBST(root->right,low,high);
returnroot;
}
};

只看代码,其实不太好理解节点是符合移除的,这一块大家可以自己在模拟模拟!

迭代法

因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。

在剪枝的时候,可以分为三步:

  • 将root移动到[L, R] 范围内,注意是左闭右闭区间
  • 剪枝左子树
  • 剪枝右子树

代码如下:

classSolution{
public:
TreeNode*trimBST(TreeNode*root,intL,intR){
if(!root)returnnullptr;

//处理头结点,让root移动到[L,R]范围内,注意是左闭右闭
while(root!=nullptr&&(root->val< L || root->val>R)){
if(root->val< L) root = root->right;//小于L往右走
elseroot=root->left;//大于R往左走
}
TreeNode*cur=root;
//此时root已经在[L,R]范围内,处理左孩子元素小于L的情况
while(cur!=nullptr){
while(cur->left&&cur->left->val< L) {
                cur->left=cur->left->right;
}
cur=cur->left;
}
cur=root;

//此时root已经在[L,R]范围内,处理右孩子大于R的情况
while(cur!=nullptr){
while(cur->right&&cur->right->val>R){
cur->right=cur->right->left;
}
cur=cur->right;
}
returnroot;
}
};

总结

修剪二叉搜索树其实并不难,但在递归法中大家可看出我费了很大的功夫来讲解如何删除节点的,这个思路其实是比较绕的。

最终的代码倒是很简洁。

如果不对递归有深刻的理解,这道题目还是有难度的!

本题我依然给出递归法和迭代法,初学者掌握递归就可以了,如果想进一步学习,就把迭代法也写一写。

其他语言版本

Java

classSolution{
publicTreeNodetrimBST(TreeNoderoot,intlow,inthigh){
if(root==null){
returnnull;
}
if(root.val< low) {
            returntrimBST(root.right,low,high);
}
if(root.val>high){
returntrimBST(root.left,low,high);
}
//root在[low,high]范围内
root.left=trimBST(root.left,low,high);
root.right=trimBST(root.right,low,high);
returnroot;
}
}

Python

classSolution:
deftrimBST(self,root:TreeNode,low:int,high:int)->TreeNode:
ifnotroot:returnroot
ifroot.val< low:
            returnself.trimBST(root.right,low,high)//寻找符合区间[low,high]的节点
ifroot.val>high:
returnself.trimBST(root.left,low,high)//寻找符合区间[low,high]的节点
root.left=self.trimBST(root.left,low,high)//root->left接入符合条件的左孩子
root.right=self.trimBST(root.right,low,high)//root->right接入符合条件的右孩子
returnroot
责任编辑:haq

  • 源代码
    +关注

    关注

    68

    文章

    2760

    浏览量

    61805
  • 二叉树
    +关注

    关注

    0

    文章

    43

    浏览量

    11074
收藏 人收藏

    评论

    相关推荐

    iCore-3568JQ-源代码-RK356X&RK3588 RKNN SDK

    iCore-3568JQ板载 RK3568J 四核 64 位工业级处理器,支持 8GB 大内存;支持....
    发表于 06-27 14:22 7次 阅读
    iCore-3568JQ-源代码-RK356X&RK3588 RKNN SDK

    液晶显示的源代码

    液晶显示的keil源代码
    发表于 06-22 15:14 10次 阅读

    基于Linux的物联网设备的安全案例

      对于物联网产品,在设计之初就将安全性构建到产品中是绝对必要的。如果您不这样做,您的业务的关键部分....
    的头像 星星科技指导员 发表于 06-22 14:28 96次 阅读

    利用Yocto/OpenEmbedded进行嵌入式软件部署

      更进一步,可以将元产品层合并到初始的 Yocto/OpenEmbedded 设置过程中,例如在我....
    的头像 星星科技指导员 发表于 06-22 14:05 119次 阅读

    TarsCpp Tars RPC框架C++语言的源代码

    ./oschina_soft/gitee-TarsCpp.zip
    发表于 06-22 10:17 10次 阅读
    TarsCpp Tars RPC框架C++语言的源代码

    GW-BASIC BASIC的方言版本

    ./oschina_soft/GW-BASIC.zip
    发表于 06-21 11:56 12次 阅读
    GW-BASIC BASIC的方言版本

    objtrace使用方法与代码分析

    我们在内核代码调试过程中,经常会遇到需要跟踪函数参数值变化的情况,objtrace是一个非常有创造性....
    的头像 Linux阅码场 发表于 06-21 09:08 118次 阅读

    确保嵌入式软件的功能安全

      静态分析不能代替硬件和软件验证,但对于防止应用程序中的干扰非常有价值。它可以在您的源代码中发现违....
    的头像 星星科技指导员 发表于 06-19 16:05 267次 阅读

    通过覆盖分析深入研究安全关键代码测试

      这种深层次的测试——以及对测试的彻底和严格的评估——只有使用一套集成的软件分析工具才能可靠地完成....
    的头像 星星科技指导员 发表于 06-19 15:55 309次 阅读
    通过覆盖分析深入研究安全关键代码测试

    需求可追溯性为彻底的软件测试奠定了基础

      在整个生命周期和整个开发工件(包括执行代码)中定义和跟踪需求的能力是能够验证代码的不可或缺的方面....
    的头像 星星科技指导员 发表于 06-19 10:41 267次 阅读
    需求可追溯性为彻底的软件测试奠定了基础

    静态分析有助于管理Java中的风险

      静态分析工具是 Java 开发人员软件开发工作中的强大盟友,因为这些工具使开发人员能够深入了解整....
    的头像 星星科技指导员 发表于 06-19 07:30 158次 阅读
    静态分析有助于管理Java中的风险

    ReShade游戏/视频后期渲染工具

    ./oschina_soft/reshade.zip
    发表于 06-17 14:38 13次 阅读
    ReShade游戏/视频后期渲染工具

    DiskANN基于图的近似近邻搜索索引

    ./oschina_soft/DiskANN.zip
    发表于 06-17 11:38 18次 阅读
    DiskANN基于图的近似近邻搜索索引

    Cython是什么,为什么会有Cython

    Cython 估计很多人都听说过,它是用来对 Python 进行加速的。如果你在使用 Python ....
    的头像 python爬虫知识分享 发表于 06-16 16:11 170次 阅读

    MovForth Forth语言编译器

    ./oschina_soft/movForth.zip
    发表于 06-16 14:40 10次 阅读
    MovForth Forth语言编译器

    为什么要设计模式

    在 Operator 条件更新上应用 Go 风格的构建器模式的实际示例
    的头像 马哥Linux运维 发表于 06-14 11:08 178次 阅读

    Fluree语义图数据库

    ./oschina_soft/db.zip
    发表于 06-13 10:22 11次 阅读
    Fluree语义图数据库

    reddit官方网站源代码

    ./oschina_soft/reddit.zip
    发表于 06-10 11:06 22次 阅读
    reddit官方网站源代码

    ByrBbsMirror北邮人论坛镜像

    ./oschina_soft/ByrBbsMirror.zip
    发表于 06-10 10:37 34次 阅读
    ByrBbsMirror北邮人论坛镜像

    OBT-BSP-SnapGear Linux S698PM平台应用开发手册

    SnapGear Linux 是一个针对嵌入式 Linux 的完整源代码包,包含 Linux 内核、....
    发表于 06-09 14:56 12次 阅读
    OBT-BSP-SnapGear Linux S698PM平台应用开发手册

    Una尤娜博客系统

    ./oschina_soft/gitee-una-boot.zip
    发表于 06-09 11:20 19次 阅读
    Una尤娜博客系统

    SystemVerilog为工程师定义新的数据类型提供了一种机制

    为了使源代码更易于阅读和维护,typedef名称应该使用一种命名约定,使名称明显代表用户自定义类型。....
    的头像 OpenFPGA 发表于 06-09 09:45 323次 阅读

    如何从源代码中寻找并修复漏洞

    专用集成电路(ASIC)的设计规模不断扩大、复杂度不断攀升,这对芯片开发者的能力和所使用的工具都提出....
    的头像 科技绿洲 发表于 06-07 16:23 464次 阅读

    在重构或重新设计时验证你的代码

      大多数组织希望通过在源代码更改时更新这些测试来保留先前测试投资的价值。但这会导致高昂的测试维护成....
    的头像 星星科技指导员 发表于 06-06 09:37 229次 阅读

    夹子机器人系统app技术开发功能详解

    区块链是不可变的,因为为交易计算SHA-256哈希。系统详情了解块的内容也会被散列,从而提供唯一的标....
    的头像 sp520110 发表于 06-02 14:45 179次 阅读

    OpenVDB电影视觉特效工具

    ./oschina_soft/openvdb.zip
    发表于 06-02 10:00 22次 阅读
    OpenVDB电影视觉特效工具

    goploader Go编写的文件共享服务

    ./oschina_soft/goploader.zip
    发表于 05-30 16:21 18次 阅读
    goploader Go编写的文件共享服务

    Plik可扩展的临时文件上传系统

    ./oschina_soft/plik.zip
    发表于 05-30 16:01 26次 阅读
    Plik可扩展的临时文件上传系统

    ssbc BT搜索引擎

    ./oschina_soft/ssbc.zip
    发表于 05-30 14:20 17次 阅读
    ssbc BT搜索引擎

    Data-Pixels通过编程创造像素图案

    ./oschina_soft/Data-Pixels.zip
    发表于 05-30 10:04 28次 阅读
    Data-Pixels通过编程创造像素图案

    BiglyBT基于Vuze的BT客户端

    ./oschina_soft/BiglyBT.zip
    发表于 05-27 09:55 22次 阅读
    BiglyBT基于Vuze的BT客户端

    配置Nginx访问日志

    每当处理客户请求时,Nginx都会在访问日志中生成一个新记录。每个事件记录都包含一个时间戳,并包含有....
    的头像 马哥Linux运维 发表于 05-24 09:59 392次 阅读

    风河公司将支持Wind River Linux系列家族新成员

    风河公司自豪地宣布商业级支持Wind River Linux系列家族新成员——Wind River ....
    的头像 科技绿洲 发表于 05-18 17:56 433次 阅读

    僵尸网络Mirai客户端

    ./oschina_soft/Mirai-Source-Code.zip
    发表于 05-09 10:34 21次 阅读
    僵尸网络Mirai客户端

    HTML_CodeSniffer违反代码标准行为检测

    HTML_CodeSniffer.zip
    发表于 04-29 10:12 43次 阅读
    HTML_CodeSniffer违反代码标准行为检测

    二叉树的最小深度

    遍历顺序上依然是后序遍历(因为要比较递归返回之后的结果),但在处理中间节点的逻辑上,最大深度很容易理....
    的头像 算法与数据结构 发表于 04-28 16:27 464次 阅读

    PrometheusAlert维告警中心消息转发系统

    gitee-PrometheusAlert.zip
    发表于 04-25 11:00 49次 阅读
    PrometheusAlert维告警中心消息转发系统

    CC2530 BasicRF源代码

    本文档描述了IEEE的CC2530片上系统解决方案的软件示例802.15.4/ZigBee。它还描述....
    发表于 04-24 09:21 105次 阅读

    C语言数据结构:什么是二叉树?

    完全二叉树:完全二叉树是效率很高的数据结构。对于深度为K,有n个节点的二叉树,当且仅当每一个节点都与....
    的头像 C语言编程学习基地 发表于 04-21 16:20 460次 阅读

    AVR单片机应用开发指南及实例

    AVR单片机应用开发指南及实例精解图书配套源代码资料分享。
    发表于 04-20 16:22 104次 阅读

    轻松玩转AVR单片机C语言源代码

    轻松玩转AVR单片机C语言图书的配套源代码资料分享。
    发表于 04-20 16:17 83次 阅读

    PIC单片机C语言程序设计实例及源代码

    PIC单片机C语言程序设计实例精粹源代码,图书的配套源代码资料分享。
    发表于 04-20 15:16 175次 阅读

    ibus输入法

    ibus.zip
    发表于 04-19 14:37 41次 阅读
    ibus输入法

    DevEco Device Tool对开发板HI3861的源代码进行编译时报错如何解决?

    我在DevEco Device Tool中按照华为官网的 鸿蒙文档对开发板HI3861(windows环境下)的源代码进行编译时报错,求各位大佬帮...
    发表于 04-18 11:10 1406次 阅读

    L3AF轻量级eBPF项目

    l3af-arch.zip
    发表于 04-15 09:33 44次 阅读
    L3AF轻量级eBPF项目

    如何用Arm平台上的源代码构建和安装带DPDK的OvS

    总览本文是“Arm上带有DPDK的Open vSwitch”系列博客的第1章。本文描述了如何用Arm平台上的源代码构建和安装带DPDK的...
    发表于 04-12 10:46 3597次 阅读

    基于openharmony适配移植实现链接跳转系统应用

    项目介绍 项目名称:Better-Link-Movement-Method 所属系列:openhar....
    发表于 04-08 10:08 46次 阅读

    如何选择合适的工具来阅读源代码

    在做嵌入式 Linux 软件开发的时候,经常会阅读大型工程源码,比如 uboot 源代码,Linux....
    的头像 strongerHuang 发表于 03-30 14:01 395次 阅读

    java实时图像与处理库教程演示

    项目介绍 项目名称:cv4j 所属系列:openharmony的第三方组件适配移植 功能:CV in....
    发表于 03-23 09:19 38次 阅读

    如何使用 go 实现红黑树

    二叉查找树也叫二叉搜索树,也叫二叉排序树,它具有以下特点:1. 如果左子树不为空,则左子树上的结点的....
    的头像 Linux爱好者 发表于 03-21 11:54 388次 阅读

    亮度可变人体感应和坐姿警告灯(含源代码和原理图及仿真)

    亮度可变人体感应和坐姿警告灯,含源代码和原理图及仿真
    发表于 03-18 11:27 119次 阅读

    GNU C库的源代码

    包含GNU C 库的源代码。请参阅文件“version.h”了解您拥有的发行版本。 GNU C库是所....
    发表于 03-18 10:26 115次 阅读

    请问要如何运行Hi3516DV300开发板的测试套件呢

      Openharmony: Hi3516DV300 怎么运行测试套件???   根据码云上的说明,运行不成功。有没有详细一点的说明???...
    发表于 03-15 13:59 1627次 阅读

    怎样去解决RTGUI中sscanf函数程序出现数据访问异常的问题呢

    小弟最近正在学习RTGUI的源代码,从stm32_radio工程应用中拷贝了一个简单的实例来测试,不过我发现在程序运行到image_xpm.c...
    发表于 03-15 09:50 1026次 阅读

    如何从源代码分析i2c驱动架构呢

    如何从源代码分析i2c驱动架构呢? 如何向i2c总线添加一个适配器? ...
    发表于 03-07 07:24 336次 阅读

    如何解决firefly rk3128 linux内核适配问题?

    如何解决firefly rk3128 linux内核适配问题?
    发表于 03-04 06:17 879次 阅读

    DAPLink源码Keil工程生成需要经过哪些步骤呢

    在Github上下载好代码后,需要先生成KEIL工程才能进行边编译下载到板子里。第一步:下载代码安装git bash或者桌面版github,...
    发表于 02-28 13:48 459次 阅读

    单片机C语言include、sfr和***it有什么差别呢

    这三个以后敲代码时一定会碰见,因此留个笔记方便以后忘了来查看。include#include 包含一个源代码文件的头文件#include //如果...
    发表于 02-25 07:20 239次 阅读

    怎样在数码管中实现显示一个数字呢

    单片机零基础入门(8-2)实战:在数码管中实现显示一个数字--数码管知识的应用一、上节回顾:二、本节需求:三、需求分析:四、源...
    发表于 02-24 07:33 514次 阅读

    详细的讲解一下I/O模式下的程序与MM模式下的程序调试

    在昨天,将I/O模式下的程序与MM模式下的程序都按照源代码修改完善后调试出来,在晚上睡觉的时候看了一点教学视频有所感悟,这...
    发表于 02-24 07:24 230次 阅读