0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

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

3天内不再提示

如何修剪二叉搜索树

算法与数据结构 来源:代码随想录 作者:程序员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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 源代码
    +关注

    关注

    96

    文章

    2945

    浏览量

    66734
  • 二叉树
    +关注

    关注

    0

    文章

    74

    浏览量

    12324

原文标题:修剪一棵二叉搜索树

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    OpenAI推出ChatGPT搜索功能

    近日,OpenAI再次迈出了重要的一步,为其广受好评的ChatGPT平台添加了一项全新的搜索功能。 据悉,这项被命名为“ChatGPT搜索”的新功能,将为用户带来前所未有的搜索体验。以往,当用户需要
    的头像 发表于 11-04 10:34 333次阅读

    OpenAI在ChatGPT增添搜索功能

    近日,OpenAI宣布为其旗舰产品ChatGPT增添全新的搜索功能,此举标志着该公司对Alphabet旗下谷歌的直接挑战进一步升级。OpenAI周四正式揭晓了这一名为“ChatGPT搜索”的新功能
    的头像 发表于 11-01 17:01 368次阅读

    谷歌取消“站点链接搜索框”,适应新搜索需求

    近日,谷歌发布了一则通知,决定取消搜索结果中的“站点链接搜索框”。这一功能已经陪伴了用户十多年,它允许用户在特定网站上进行更深入的搜索,为许多网民提供了便利。然而,随着时代的变迁和技术的进步,这一
    的头像 发表于 10-23 11:20 332次阅读

    什么是默克尔(Merkle Tree)?如何计算默克尔根?

    01 默克尔的概念 默克尔(Merkle Tree)是一种特殊的二叉树,它的每个节点都存储了一个数据块的哈希值。哈希值是一种可以将任意长度的数据转换为固定长度的字符串的算法,它具有唯一性和不可
    的头像 发表于 09-30 18:22 850次阅读
    什么是默克尔<b class='flag-5'>树</b>(Merkle Tree)?如何计算默克尔根?

    月访问量超2亿,增速113%!360AI搜索成为全球增速最快的AI搜索引擎

    和系统自动匹配最佳模型,这使得360AI搜索获得了独一无的技术优势。除了通用大模型,360AI搜索还配备了众多搜索场景专用模型,精准提升特定场景下的
    的头像 发表于 09-09 13:44 466次阅读
    月访问量超2亿,增速113%!360AI<b class='flag-5'>搜索</b>成为全球增速最快的AI<b class='flag-5'>搜索</b>引擎

    AI搜索新贵弯道超车难

    新玩家高调入场,老玩家默默升级,搜索市场进入新一轮洗牌阶段。最近一段时间,老旧的搜索行业开出了新花。从2009年开始,谷歌、百度成了搜索领域绕不开的存在,它们占据了全球搜索引擎市场绝大
    的头像 发表于 07-09 08:05 231次阅读
    AI<b class='flag-5'>搜索</b>新贵弯道超车难

    指电极上覆盖敏感材料的阻值计算

    覆盖的敏感材料厚度超出指厚度时计算电阻,是否可以视作指电极指间电阻多个周期串联后与超出指厚度部分敏感材料电阻并联
    发表于 07-05 14:48

    指MOSFET器件静电防护鲁棒性提升技巧

    栅极接地NMOS是一种广泛应用的片上ESD器件结构,为达到特定ESD防护等级,一般会采用多指版图形式来减小器件占用的芯片面积。但是,多指栅极接地NMOS在ESD应力作用下,各个指难于做到均匀
    的头像 发表于 06-22 00:50 514次阅读
    多<b class='flag-5'>叉</b>指MOSFET器件静电防护鲁棒性提升技巧

    AI商业化的考卷,360选了搜索来答

    广告之外的第条路。随着AI技术的发展,搜索引擎领域正经历巨大变革。截至今年4月,美国人工智能搜索公司PerplexityAI上线仅15个月,访问量已突破10亿次,迅速成为谷歌的重要竞争对手。国内
    的头像 发表于 06-16 08:04 166次阅读
    AI商业化的考卷,360选了<b class='flag-5'>搜索</b>来答

    原理图设计里两颗重要的(国产EDA)

    原理图里面两颗重要的,那就是元件和网络,作为EDA工具中的重要视图和概念,虽然看似枯燥,但它们扮演着非常重要的角色,它们为电路图的层次化结构提供了有力支撑。想象一个大型的电路设计项目,就像一个
    的头像 发表于 05-29 17:47 742次阅读
    原理图设计里两颗重要的<b class='flag-5'>树</b>(国产EDA)

    圣诞灯电路图分享

    圣诞装饰的电路分为两个主要部分,即灯光和声音部分。照明部分由五组 LED 组成,它们以进制顺序运行,每隔几分钟就会重复一次。在这里,根据我们的兴趣,LED 可以是任何颜色。这件装饰品可以装饰您的圣诞以及您的家。
    的头像 发表于 05-05 10:12 952次阅读
    圣诞<b class='flag-5'>树</b>灯电路图分享

    微软推出Edge搜索栏,提升用户搜索效率

    据4月19日消息,微软近期推出Windows 11与Windows 10系统更新,新增Edge搜索栏桌面集成功能。官方表示,此举旨在为用户提供更便捷的搜索体验,无需开启浏览器即可获得所需信息,从而提升工作效率。
    的头像 发表于 04-19 14:44 681次阅读

    Redis官方搜索引擎来了,性能炸裂!

    RediSearch 是一个 Redis 模块,为 Redis 提供查询、级索引和全文搜索功能。
    的头像 发表于 02-21 10:01 2329次阅读
    Redis官方<b class='flag-5'>搜索</b>引擎来了,性能炸裂!

    鸿蒙OS开发之 融合搜索概述

    HarmonyOS 融合搜索为开发者提供搜索引擎级的全文搜索能力,可支持应用内搜索和系统全局搜索,为用户提供更加准确、高效的
    的头像 发表于 01-29 16:24 561次阅读
    鸿蒙OS开发之  融合<b class='flag-5'>搜索</b>概述

    语音数据集在智能语音搜索中的应用与挑战

    挥着重要作用,为系统提供了丰富的语音数据和信息,提高了搜索的准确性和效率。本文将详细介绍语音数据集在智能语音搜索中的应用、面临的挑战以及未来的发展趋势。 、语音数据集在智能语音搜索
    的头像 发表于 01-18 15:09 546次阅读