算法训练day18 | php | 669. 修剪二叉搜索树 , 108.将有序数组转换为二叉搜索树 , 538.把二叉搜索树转换为累加树

文章介绍了如何使用递归算法对二叉搜索树进行修剪,使其所有节点值位于特定范围内,以及如何将有序数组转换为高度平衡的二叉搜索树。此外,还讲解了如何将二叉搜索树转换为累加树,使每个节点值等于大于或等于其值的节点之和。

一、力扣题669. 修剪二叉搜索树

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

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

示例 1:

输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]

示例 2:

输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]

提示:

  • 树中节点数在范围 [1, 104] 内
  • 0 <= Node.val <= 104
  • 树中每个节点的值都是 唯一 的
  • 题目数据保证输入是一棵有效的二叉搜索树
  • 0 <= low <= high <= 104

         这道题同样是涉及改变二叉树结构的,先列举一下有哪些情况,已知最小边界为 low,最大边界为 high。 当一个节点值在 [ low, high ] 区间之外说明该节点需要被删除,但该节点的子节点又可能符合 [ low,high] 区间,比如例2,所以不能直接删除这个节点及其子节点,只能删除该节点并让子节点补位。

        以下列举情况:

        1、节点在 [ low, high ] 区间内,继续遍历其左右节点。

        2、节点为空,返回 null。

        3、节点值小于 low,说明该节点的左子树全都不符合条件,所以直接让其右节点取代该节点,并继续递归右节点。

        4、节点值大于 high,说明该节点的右子树全都不符合条件,直接让左节点取代该节点,继续递归左节点。

        情况列举清楚了,如何处理被删除节点的子节点也就清楚了。接下来就只剩一个问题,如何让子节点取代被删除的节点,明显是用  $root -> left 或者 $root -> right 来指向递归左右节点后返回的值,如果递归的左节点要被删除,返回的就不是左节点,而是左节点的子节点,那么  $root -> left 指向的就是左节点的子节点,这样自然而然地就删除了左节点。

        这种处理方式比较难想到,要努力熟悉这种应用,某种程度上这种也是一种自下而上的逻辑。

function trimBST($root, $low, $high) {
        if($root === null) return null;
        if($root->val > $high) {
            return $this->trimBST($root->left, $low, $high);
        }
        if($root->val < $low) {
            return $this->trimBST($root->right, $low, $high);
        }

        $root->left = $this->trimBST($root->left, $low, $high);
        $root->right = $this->trimBST($root->right, $low, $high);
        return $root;
    }

二、力扣题108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

示例 1:

输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 2:

输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 按 严格递增 顺序排列

        这道题和之前的 “用前序和后序序列构造二叉树” 有点像,都是要先充分了解构造过程,再来填入递归的逻辑中。

         将一个升序数组转化为二叉搜索树,并且要求是高度平衡的二叉搜索树,即每个节点的左右子树高度差不能超过1。

        那么每次取中节点时,都要取数组最中间的值,这样中节点两边的数组个数的绝对差才能不超过1。

        递归参数:数组; 终止条件:数组为空;返回参数:构造的中节点。

        单层递归逻辑:获取数组最中间的值,将该值赋给一个新节点 cur ,将这个值左右两边的数组都切割出来,cur -> left  和  cur -> right 分别存放递归这两个左右数组返回的值,返回节点 cur。

function sortedArrayToBST($nums) {
        if($nums == []) return null;
        $sub = intval(count($nums) / 2);
        $cur = new TreeNode($nums[$sub]);

        $cur->left = $this->sortedArrayToBST(array_slice($nums, 0, $sub));
        $cur->right = $this->sortedArrayToBST(array_slice($nums, $sub + 1));
        return $cur;
    }

三、力扣题538. 把二叉搜索树转换为累加树

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意:本题和 1038: 力扣 相同

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]
输出:[1,null,1]

示例 3:

输入:root = [1,0,2]
输出:[3,3,2]

示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

提示:

  • 树中的节点数介于 0 和 104 之间。
  • 每个节点的值介于 -104 和 104 之间。
  • 树中的所有值 互不相同 。
  • 给定的树为二叉搜索树。

         累加树的定义是:每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

        而如果中序遍历二叉搜索树,遍历的节点的值一定是从小到大的,反过来用 “右中左” 的顺序遍历二叉搜索树,遍历的节点的值一定是从大到小的。

        将按“右中左”顺序遍历节点的值先设定成一个降序数组,数组中的值如果要符合累加树的特性,那数组中的每个值都要加上这个值前面的所有值,比如 arr [3] = arr [0] + arr [1] + arr [2],这样来处理就简单多了。

        递归参数:节点;终止条件:节点为空;无返回参数。

        设置一个全局变量 sum 用于存放比当前节点大的所有节点值的总和。

        单层递归逻辑:先递归当前节点的右节点,将 sum 的值加上当前节点的值,再把 sum 赋给当前节点,最后递归当前节点的左节点。

class Solution {
    private $sum = 0;
    /**
     * @param TreeNode $root
     * @return TreeNode
     */
    function convertBST($root) {
        if(!$root) return null;

        $this->convert($root);
        return $root;
    }

    function convert($root) {
        if(!$root) return;

        $this->convert($root->right);

        $this->sum += $root->val;
        $root->val = $this->sum;

        $this->convert($root->left);
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值