单纯移除一个节点那还不够,要修剪!

669. 修剪二叉搜索树

题目链接:https://leetcode-cn.com/problems/trim-a-binary-search-tree/

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

思路

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

但还真的不简单!

递归法

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

不难写出如下代码:

class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root == nullptr || root->val < low || root->val > high) return nullptr;root->left = trimBST(root->left, low, high);root->right = trimBST(root->right, low, high);return root;}
};

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

我们在重新关注一下第二个示例,如图:

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

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

其实不用重构那么复杂。

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

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

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

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

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

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

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

代码如下:

TreeNode* trimBST(TreeNode* root, int low, int high)

  • 确定终止条件

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

if (root == nullptr ) return nullptr;

  • 确定单层递归的逻辑

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

代码如下:

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

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

代码如下:

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

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

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

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

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

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

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

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

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

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

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

最后整体代码如下:

class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root == nullptr ) return nullptr;if (root->val < low) {TreeNode* right = trimBST(root->right, low, high); // 寻找符合区间[low, high]的节点return right;}if (root->val > high) {TreeNode* left = trimBST(root->left, low, high); // 寻找符合区间[low, high]的节点return left;}root->left = trimBST(root->left, low, high); // root->left接入符合条件的左孩子root->right = trimBST(root->right, low, high); // root->right接入符合条件的右孩子return root;}
};

精简之后代码如下:

class Solution {
public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root == nullptr) return nullptr;if (root->val < low) return trimBST(root->right, low, high); if (root->val > high) return trimBST(root->left, low, high); root->left = trimBST(root->left, low, high);root->right = trimBST(root->right, low, high);return root;}
};

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

迭代法

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

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

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

代码如下:

class Solution {
public:TreeNode* trimBST(TreeNode* root, int L, int R) {if (!root) return nullptr;// 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭while (root->val < L || root->val > R) {if (root->val < L) root = root->right; // 小于L往右走else root = 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;}return root;}
};

总结

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

最终的代码倒是很简洁。

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

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

「就酱,如果学到了,就转发给身边需要的同学吧!」

本文:

https://github.com/youngyangyang04/leetcode-master​github.com

已经收录,里面还有leetcode刷题攻略、各个类型经典题目刷题顺序、思维导图,可以fork到自己仓库,有空看一看一定会有所收获,如果对你有帮助也给一个star支持一下吧!

我的B站(里面有我讲解的算法视频已经编程相关知识):

哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili​space.bilibili.com

我是程序员Carl,哈工大师兄,先后在腾讯和百度从事技术研发多年,利用工作之余重刷leetcode,更多精彩算法文章尽在公众号:代码随想录,关注后,回复「Java」「C++」「python」「简历模板」等等,有我整理多年的学习资料,可以加我微信,备注「个人简介」+「组队刷题」,拉你进入刷题群,每天一道经典题目分析,我选的每一道题目都不是孤立的,而是由浅入深一脉相承的,如果跟住节奏每篇连续着看,定会融会贯通。

消除左递归实验代码_「leetcode」669. 修剪二叉搜索树:【递归】【迭代】详解!相关推荐

  1. 消除左递归实验代码_「leetcode」108. 构造二叉搜索树【递归】【迭代】详解!

    构造二叉搜索树,一不小心就平衡了 ❞ 108.将有序数组转换为二叉搜索树 将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树. 本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树 ...

  2. 消除左递归c++代码_「leetcode」129. 求根到叶子节点数字之和【递归中隐藏着回溯】详解...

    链接 https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/ 思路 本题和113.路径总和II是类似的思路,做完这道题,可以顺便把113. ...

  3. LeetCode 669. 修剪二叉搜索树(Trim a Binary Search Tree)

    669. 修剪二叉搜索树 669. Trim a Binary Search Tree 题目描述 LeetCode LeetCode669. Trim a Binary Search Tree简单 J ...

  4. LeetCode 669. 修剪二叉搜索树

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

  5. 68. Leetcode 669. 修剪二叉搜索树 (二叉搜索树-基本操作类)

    给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high.通过修剪二叉搜索树,使得所有节点的值在[low, high]中.修剪树 不应该 改变保留在树中的元素的相对结构 (即, ...

  6. python中的猜数字实验代码_「gals」python实现推理猜数字游戏Begals - seo实验室

    gals Begals是可以和朋友一起玩的一个推理游戏.你的朋友想到一个随机的.没有重复的3位数字,你尝试去猜测它是什么. 每次猜测之后,朋友就会给出3种类型的线索: Bagels:你猜测的3个数都不 ...

  7. LeetCode 95. 不同的二叉搜索树 II(递归)

    1. 题目 给定一个整数 n,生成所有由 1 - n 为节点所组成的二叉搜索树. 示例: 输入: 3 输出: [[1,null,3,2],[3,2,null,1],[3,1,null,null,2], ...

  8. LeetCode 96. 不同的二叉搜索树(DP)

    1. 题目 给定一个整数 n,求以 1 - n 为节点组成的二叉搜索树有多少种? 示例:输入: 3 输出: 5 解释: 给定 n = 3, 一共有 5 种不同结构的二叉搜索树:1 3 3 2 1\ / ...

  9. Leetcode 96. 不同的二叉搜索树

    Leetcode 96. 不同的二叉搜索树 1.问题分析 2.问题解决 3.总结 1.问题分析 题目链接:https://leetcode-cn.com/problems/unique-binary- ...

最新文章

  1. [Spring mvc 深度解析(一)] 详解Servlet
  2. vscode 免费视频教程
  3. POJ3268 Silver Cow Party(最短路径)
  4. windows2003中mssql连接的NT AUTHORITY\NETWORK SERVICE登录失败的问题
  5. kettle的安装与连接mysql(包含mysql8)简单使用,
  6. 数学之美 系列七 -- 信息论在信息处理中的应用
  7. boost::mp11::mp_reverse_fold相关用法的测试程序
  8. SpringSecurity 权限控制准备之IOC容器结构说明
  9. SpringBoot使用velocity模板引擎
  10. python中时间处理_Python中处理时间的几种方法小结
  11. Java NIO学习篇之NIO的基本认识
  12. linux下进程调度算法实验,Linux下进程调度算法的模拟实现.doc
  13. 可控硅型号怎样识别_可控硅是什么_可控硅型号_可控硅分类及判别_可控硅种类...
  14. c# 链接mongDB集群实战开发3
  15. CentOS 6系统FreeSwitch和RTMP服务 安装及演示(三)
  16. mysql临键锁_innodb临键锁锁定范围
  17. 制作半透明的毛玻璃效果教程
  18. Flutter使用Clip裁剪Widget
  19. ​区块链公链“三元悖论”专题系列之去中心化(Decentralization)
  20. Java多线程学习详细学习及扩展

热门文章

  1. 分享两个常见的搜索算法:BFS和DFS
  2. 鸿蒙轻内核源码分析:虚实映射
  3. 5月20日,GaussDB将有大事发生
  4. 解密昇腾AI处理器--DaVinci架构(总览)
  5. 程序员一定要知道的11个实用工具网站
  6. 使用CSE实现滚动升级零中断
  7. 红橙Darren视频笔记 热更新 bsdiff bspatch 在Android设备上的应用 架构篇1完结篇
  8. 红橙Darren视频笔记 界面优化与屏幕适配(下)
  9. java 运行管理-基础
  10. 产生信号的代码10分类