二叉树:修剪二叉搜索树

修剪二叉搜索树leetcode题目链接

一、题目描述

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

实例
请添加图片描述

二、解题思路

修剪二叉树的意思就是,将不在给定范围内的结点删除,并继续保持二叉搜索树。

注意,删除之后还要保持二叉搜索树,这非常非常重要,是这个题的难点和核心。

第一个问题:如果遇到一个结点我们要删除,那这个结点可能出现在哪里?

很简单:要么删除的是叶子结点,要么删除的不是叶子结点

1.先来讨论删除的是叶子结点的情况:

首先看这个树,要保留[3, 6] 之间的内容,我们要删除的就是2, 7结点了,那具体怎么处理呢?

请添加图片描述

只要将2, 7这两个结点换成NULL就行了。

2.再来讨论删除的是非叶子结点的情况:

请添加图片描述

这里删除的是这棵树[1, 4]以外的结点,如图,要删除的就是0和4这两个结点。

对于删除结点4前面讨论过,将4替换成NULL就行了。

那0结点显然是不能用NULL直接来代替的,那应该怎么操作呢?

首先,对于0这个结点,是小于[1, 4]的low的,所以,如果0这个结点被删除了,就不是一个合格的二叉搜索树了。

应该怎么做呢?

就应该被它右子树中找一个在[1,4]内的子树,用这个子树来代替这个几点。

具体怎么操作呢?

然后对0的右子树进行递归检查,目的是检查0的右子树中的结点是否都是在[1, 4]范围内的,如果,0的右子树都是在[1, 4]内的,说明我们可以直接用0的右子树来代替0,修剪就完成了。

一、递归检查以2为根的子树,首先检查2,发现是在[1, 4]内的
二、递归检查以1为根的子树,首先检查1,发先是在[1, 4]内的
三、以2为根的子树检查完毕,即0的左子树都是在[1, 4]内的

所以,用0的右子树来代替0就可以了。

同样的道理,如果不是这个树,是别的树,对这个树的每一个结点/子树都可以做如下判断

  1. 判断这个结点和[1, 4]的大小
    1. 如果小于1,就在这个结点的右子树中找到一个合格的树来代替这个结点
    2. 如果大于4,就在这个结点的左子树中找到一个合格的树来代替这个结点

三、代码解析

二叉树递归算法的逻辑和代码结合比较难理解。一定要结合代码和例子,自己手动推导下具体例子在代码中运行的流程。代码下面还有例子。

TreeNode* trimBST(TreeNode* root, int low, int height){
	//如果检测到了NULL,也就是都检测到叶子结点的孩子了,或者这个树是空树,就返回NULL
	if (root == NULL) return NULL;

	//判断当前检测的这个结点是否小于low
	if (root->val < low) {
		//如果这个结点的val小于low,就要去这个结点的右子树去找,
		//找一个合格的子树来代替当前结点
		TreeNode* right = trimBST(root->right, low, height);
		return right;//如何让右子树代替当前结点呢,就是将递归找到的那个盒子子树根返回
		//这行代码要和下面*代码共同理解
	}

	//判断当前检测的这个结点是否大于height(逻辑和root->val < low 类似)
	if (root->val > height) {
		TreeNode* left = trimBST(root->left, low, height);
		return left;
	}

	//如果这个结点在[low, height]范围内,那就递归去这个结点的左子树去寻找合规的子树
	//并将这个修剪过的子树返复制个当前结点的左子树,当然,如果这个结点所有左子树都合规
	//那么,这个结点的左子树也会被原封不动的返回过来
	root->left = trimBST(root->left, low, height);
	//以咱们上面例子距离,根节点的左子树最后的返回值就是修改后的子树
	root->right = trimBST(root->right, low, height);//和上面同理
	return root;
}

这里还是以这个树为例子,将例子结合代码手推导。

请添加图片描述

  1. 首先检查3是否是NULL
  2. 检查3是否小于low
  3. 检查3是否大于height
  4. 给3的左子树赋值为左子树修递归剪过后的子树
    1. 首先检查0是否是NULL
    2. 检查0是否小于low
    3. 去0的右子树找递归修剪过的子树
      1. 检查2是否为NULL
      2. 检查2是否小于low
      3. 检查2是否大于height
      4. 给2的左子树赋值为左子树递归修剪过后的子树
        1. 首先检查1是否为NULL
        2. 检查1是否小于low
        3. 检查1是否大于height
        4. 给1的左值树赋值为左子树递归修剪过后的子树
          1. 检查1的左子树为NULL
            1. 返回NULL
          2. 1的左子树赋值为返回的NULL
          3. 返回1为根的子树
        5. 2的左子树赋值为返回的1
        6. 返回2为根的子树
    4. 返回0修剪过的左子树
  5. 3的左子树赋值0修剪过的左子树
  6. 3的右子树赋值为右子树递归修剪过后的子树(右子树逻辑简单这里就不展开)
  7. 返回3为根的树
  8. 结束

注意,所有黄色高亮都是本层递归的返回,每一次缩进代表一层递归。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛伯达岛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值