题目描述

✅ 1038. 从二叉搜索树到更大和树

image-20260928224233101

image-20260928224233103

题意分析

将二叉搜索树中每个节点的值改为:它原来的值,加上原树中所有比它大的节点值之和。题目节点值互不相同,因此也可理解为累加原树中所有不小于当前值的节点。

必须依据修改前的值求和,节点数量和左右连接关系保持不变,返回修改后的原根节点。改值后的树不要求继续满足二叉搜索树的大小关系。

解法:反向中序遍历累计和

核心思路

[!blue]

BST 中序遍历按值从小到大访问,将顺序反过来改成右子树、当前节点、左子树,就会按原值从大到小访问。处理当前节点时,恰好已经访问了所有比它大的节点。

用贯穿整次遍历的累计量 sum 保存已访问节点的原值之和。先执行 sum += node.val,把当前节点尚未修改的值加入,再执行 node.val = sum,当前节点就得到自身与所有更大原值之和。

之后递归左子树,刚加入的当前原值也应成为这些更小节点的贡献,因此 sum 不能在子树之间重置或各自复制。空节点直接返回,不贡献数值。

虽然已访问节点的值已经变了,后续遍历只沿原来的左右指针,不再靠修改后的值决定方向,也不重新读取它们计数;它们的原值贡献已经保存到 sum。只有每次处理一棵新输入树时才将累计量归零,避免复用对象带入上次结果。

解题步骤

  1. 在方法入口将累计和初始化为零。
  2. 递归遇到空节点时返回,否则先处理右子树的较大原值。
  3. 将当前旧值加入累计和,再把累计和写回当前节点。
  4. 继续处理左子树,完成后返回原根节点。

代码实现

class Solution {
    private int sum;

    public TreeNode bstToGst(TreeNode root) {
        // 每棵输入树独立累计,避免沿用上次结果。
        sum = 0;
        traverse(root);

        return root;
    }

    private void traverse(TreeNode node) {
        if (node == null) {
            return;
        }

        traverse(node.right);

        // 更大的原值已累计,先加入自身旧值再写回。
        sum += node.val;
        node.val = sum;

        traverse(node.left);
    }
}
func bstToGst(root *TreeNode) *TreeNode {
    // 每棵输入树独立累计,避免沿用上次结果。
    sum := 0

    var traverse func(node *TreeNode)
    traverse = func(node *TreeNode) {
        if node == nil {
            return
        }
        traverse(node.Right)

        // 更大的原值已累计,先加入自身旧值再写回。
        sum += node.Val
        node.Val = sum

        traverse(node.Left)
    }

    traverse(root)
    return root
}

复杂度分析

  • 时间复杂度:$O(n)$。
  • 空间复杂度:$O(h)$,最坏链状为 $O(n)$。

关键点总结

[!green]

  • 反向中序提供原值递减顺序,使累计和恰好包含当前值所需的全部贡献。
  • 先累加旧值再覆盖,保证每个原值只贡献一次。
  • 累计量在一棵树的各子树间共享,在不同输入树之间重新初始化。
  • 只改节点值,遍历顺序由原树连接关系决定。

易错点总结

[!yellow]

  • 先覆盖节点再累加,会丢掉它自己的原值。
  • 普通的左、根、右顺序会累计较小节点,方向与题意相反。
  • 在每个子树内部重新清零,会遗漏子树外已经访问的更大原值。
  • Java 同一对象再次处理另一棵原始树时,需要在入口重置字段累计量。

相似题目

题目 难度 关联与区别
230. 二叉搜索树中第 K 小的元素 中等 同样利用BST的中序顺序,本题改为逆中序累计更大值,原题按升序定位第k项。
897. 递增顺序搜索树 简单 同样通过有序遍历改造BST,原题重连节点但保留值,本题修改值而保留树结构。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2021/08512264
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!