讲师中心 微信公众号
AI工具推荐 视频效率加速

深入理解LeetCode 1038:将二叉搜索树转换为累加树

风涛小哥_9570

风涛小哥_9570

发布时间:2025-11-21 17:21:06

|

737人浏览过

|

来源于php中文网

原创

深入理解leetcode 1038:将二叉搜索树转换为累加树

本文详细解析了如何将二叉搜索树(BST)转换为累加树(Greater Tree),即每个节点的值更新为原节点值加上所有大于或等于该节点值的总和。核心算法利用了二叉搜索树的特性,通过反向中序遍历(右-根-左)结合累加和,以递归方式高效地完成转换,并重点阐释了递归调用中累加值传递的机制。

理解累加树的转换原理

将二叉搜索树转换为累加树(Greater Tree)是指对树中的每个节点,将其值更新为原节点值加上所有大于或等于该节点值的节点之和。由于二叉搜索树的特性——左子树所有节点的值小于根节点,右子树所有节点的值大于根节点,我们可以利用这一特性进行高效的转换。

实现这一转换的关键在于采用反向中序遍历(Right -> Root -> Left)策略。当我们从右子树开始遍历时,遇到的节点值总是大于或等于其左侧的节点。因此,我们可以维护一个累加和,在遍历过程中不断更新这个和,并将其加到当前节点的值上。

核心算法解析

以下是实现累加树转换的JavaScript代码示例:

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
const bstToGst = (root) => {
    // 辅助函数,用于递归遍历和更新节点值
    // sum 参数用于传递当前已经累加的“更大”值之和
    function go(node, sum) {
        // 1. 基本情况:如果当前节点为空,则返回当前的累加和。
        // 这是递归的终止条件,也是累加和向上返回的机制。
        if (!node) {
            return sum;
        }

        // 2. 遍历右子树:首先处理右子树,因为右子树中的所有节点都比当前节点大。
        // go(node.right, sum) 会返回右子树遍历完成后,所有已处理的更大节点值的总和。
        // 这个总和(包括右子树的所有节点和从父节点传递下来的 sum)被加到当前节点 node.val 上。
        node.val += go(node.right, sum);

        // 3. 遍历左子树:处理完当前节点及其右子树后,当前 node.val 已经包含了
        // 自身值以及所有比它大的节点值(来自右子树和初始 sum)。
        // 现在,将这个更新后的 node.val 作为新的累加和传递给左子树。
        // 左子树中的所有节点都比当前 node 小,但它们需要加上当前 node 及其右子树的值。
        // 因此,返回 go(node.left, node.val) 的结果,确保这个累加和继续向下传递。
        return go(node.left, node.val);
    }

    // 从根节点开始调用辅助函数,初始累加和为0
    go(root, 0);
    // 返回转换后的根节点
    return root;
};

关键语句 return go(root.left, root.val); 的作用

在上述代码中,return go(root.left, root.val); 语句是理解累加树转换机制的核心之一。其作用可以分解为以下几点:

  1. 传递更新后的累加和: 当执行到这一行时,root.val 已经完成了更新。它现在包含了:

    jinn-node
    jinn-node

    在Jinn网络为自主项目工作赚取代币奖励,让闲置的OpenClaw代理开始工作。

    下载
    • root 节点原始值。
    • 所有位于 root 右子树中的节点值(因为 go(root.right, sum) 的结果已经被加到 root.val 上)。
    • 从 root 的父节点或初始调用传递下来的 sum 值(代表比 root 及其右子树所有节点都大的值)。 这个更新后的 root.val 正是 root 及其所有比它大的节点值的总和。
  2. 继续遍历左子树: 根据二叉搜索树的特性,root.left 子树中的所有节点都小于 root 节点。因此,在处理 root.left 子树时,我们需要确保这些较小的节点也能累加上 root 节点自身以及所有比 root 大的节点的值。通过将更新后的 root.val 作为新的 sum 参数传递给 go(root.left, root.val),我们确保了左子树中的每个节点在更新时,都会包含这个“更大”的累加值。

  3. 递归调用的返回值: go 函数的返回值始终是当前子树(或整个树)遍历完成后,下一个需要累加的“更大”值之和。当 go(root.left, root.val) 执行完毕并返回时,它实际上返回的是在处理完 root 的左子树后,继续向上层(root 的父节点)或更上层传递的累加和。这个返回值最终会传递到最初的 go(root, 0) 调用,但由于我们只关心对树的修改,最终 bstToGst 函数直接返回了被修改的 root。

示例跟踪

让我们用一个例子来跟踪 sum 的传递和节点值的更新:

      4
    /   \
  1       6
 / \     / \
0   2   5   7
     \       \
      3       8

初始调用 go(root=4, sum=0)

  1. go(4, 0):
    • 调用 go(6, 0) (右子树)
      • go(6, 0):
        • 调用 go(7, 0) (右子树)
          • go(7, 0):
            • 调用 go(8, 0) (右子树)
              • go(8, 0):
                • 调用 go(null, 0) -> 返回 0
                • 8.val += 0 -> 8.val = 8
                • 调用 go(null, 8) -> 返回 8
              • 8 返回到 go(7, 0)
            • 7.val += 8 -> 7.val = 15
            • 调用 go(null, 15) -> 返回 15
          • 15 返回到 go(6, 0)
        • 6.val += 15 -> 6.val = 21
        • 调用 go(5, 21) (左子树)
          • go(5, 21):
            • 调用 go(null, 21) -> 返回 21
            • 5.val += 21 -> 5.val = 26
            • 调用 go(null, 26) -> 返回 26
          • 26 返回到 go(6, 0)
        • 26 返回到 go(4, 0)
      • 4.val += 26 -> 4.val = 30
      • 调用 go(1, 30) (左子树)
        • go(1, 30):
          • 调用 go(2, 30) (右子树)
            • go(2, 30):
              • 调用 go(3, 30) (右子树)
                • go(3, 30):
                  • 调用 go(null, 30) -> 返回 30
                  • 3.val += 30 -> 3.val = 33
                  • 调用 go(null, 33) -> 返回 33
                • 33 返回到 go(2, 30)
              • 2.val += 33 -> 2.val = 35
              • 调用 go(null, 35) -> 返回 35
            • 35 返回到 go(1, 30)
          • 1.val += 35 -> 1.val = 36
          • 调用 go(0, 36) (左子树)
            • go(0, 36):
              • 调用 go(null, 36) -> 返回 36
              • 0.val += 36 -> 0.val = 36
              • 调用 go(null, 36) -> 返回 36
            • 36 返回到 go(1, 30)
          • 36 返回到 go(4, 0)
        • 36 返回到初始调用

最终,树的节点值会被正确更新。

注意事项与总结

  1. 二叉搜索树特性: 此算法高度依赖于二叉搜索树的有序性。如果输入不是二叉搜索树,则结果将不正确。
  2. 反向中序遍历: 理解右-根-左的遍历顺序是关键。它确保了在处理当前节点时,所有比它大的节点(位于其右子树或更上层的右侧)的值已经累加到 sum 中。
  3. 累加和的传递: sum 参数在递归调用中起到了至关重要的作用。它不仅在向下传递时携带了已处理的更大节点值之和,而且在从递归调用返回时,也返回了当前子树处理完毕后的最新累加和,供上层节点继续使用。
  4. 时间与空间复杂度:
    • 时间复杂度: O(N),其中 N 是树中节点的数量。每个节点只访问一次。
    • 空间复杂度: O(H),其中 H 是树的高度。这主要是由递归调用栈的深度决定。在最坏情况下(树退化为链表),H 可以是 N。

通过深入理解反向中序遍历和累加和的传递机制,我们可以高效且优雅地将二叉搜索树转换为累加树。

热门AI工具

更多
火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

509

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1638

2024.03.01

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

4547

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2088

2023.08.10

java值传递和引用传递有什么区别
java值传递和引用传递有什么区别

java值传递和引用传递的区别:1、基本数据类型的传递;2、对象的传递;3、修改引用指向的情况。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

166

2024.02.23

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

4656

2023.08.14

Buffalo框架数据库开发全教程
Buffalo框架数据库开发全教程

本专题围绕Buffalo框架数据库开发,讲解database.yml多环境配置、soda与fizz迁移生成回滚、模型结构体标签、增删改查与条件查询、一对多与多对多关联、数据校验、回调钩子、事务处理及原生SQL执行能力。

0

2026.09.23

Buffalo框架路由与请求处理实操指南
Buffalo框架路由与请求处理实操指南

本专题讲解Buffalo框架路由与请求处理机制,涵盖路由注册与分组、资源路由、Handler编写规范、Context上下文方法、参数绑定、中间件编写挂载、Session与Cookie读写、Flash消息及错误页面定制方法。

0

2026.09.23

Buffalo框架零基础入门教程
Buffalo框架零基础入门教程

本专题整理Buffalo框架入门内容,涵盖Go环境准备、buffalo CLI安装、新项目生成、目录结构说明、dev热加载启动、数据库连接配置与常见报错排查,帮助新手按约定优于配置的思路跑通第一个Buffalo框架应用。

0

2026.09.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebStorm 官方调试文档
WebStorm 官方调试文档

共0课时 | 0人学习

React 教程
React 教程

共58课时 | 11.9万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 6.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn