新闻中心

深入理解 LeetCode 1038:利用反向中序遍历将二叉搜索树转换为累加树

2025-11-21
浏览次数:
返回列表

深入理解 leetcode 1038:利用反向中序遍历将二叉搜索树转换为累加树

本教程深入探讨 LeetCode 1038 题,讲解如何将二叉搜索树(BST)转换为累加树(Greater Tree)。核心方法是利用 BST 的特性,通过一次反向中序遍历(右-根-左)来高效更新节点值。文章将详细解析递归函数的运作机制,特别是 `return go(root.left, root.val)` 语句如何确保累加和的正确传递,并提供示例代码和解释,帮助读者掌握这一经典算法模式。

在二叉搜索树(BST)中,每个节点的值都大于其左子树中的所有节点值,且小于其右子树中的所有节点值。将 BST 转换为累加树(Greater Tree)的目标是使每个节点的新值等于其原始值加上所有大于或等于其原始值的节点值之和。例如,如果一个 BST 节点值为 X,转换后它的新值将是 X + (所有大于 X 的节点值之和)。

核心思想:反向中序遍历

解决此问题的关键在于利用 BST 的有序性。如果我们按照从大到小的顺序遍历 BST 中的所有节点,就可以在遍历过程中累加一个总和,并将这个总和加到当前节点上。这种从大到小的遍历顺序恰好是“反向中序遍历”(Right -> Root -> Left)。

  1. 访问右子树: 首先访问右子树,因为右子树中的所有节点都比当前根节点大。
  2. 访问根节点: 接着访问根节点。此时,我们已经处理了所有比根节点大的节点(即右子树中的节点),并累加了它们的和。我们将这个累加和加到根节点上。
  3. 访问左子树: 最后访问左子树。左子树中的所有节点都比当前根节点小,但它们仍然需要加上之前累积的总和(包括根节点及其右子树的和)。

算法实现与代码解析

以下是实现这一转换的 J*aScript 代码:

/**
 * 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. 递归处理右子树:
        // 先处理右子树,并将右子树处理完后返回的累加和(即所有比当前节点大的节点值之和)
        // 加到当前节点的 val 上。
        // go(node.right, sum) 返回的是处理完右子树后,最新的累加和。
        node.val += go(node.right, sum);

        // 3. 递归处理左子树:
        // 当前节点的 val 已经更新为“原始值 + 所有比它大的节点值之和”。
        // 这个更新后的 node.val 就是新的累加和,需要传递给左子树。
        // 因为左子树中的所有节点都比当前 node 小,但它们需要加上 node 当前的累加值。
        // go(node.left, node.val) 将处理左子树,并返回处理完左子树后,最新的累加和。
        // 这个返回值将继续向上传递。
        return go(node.left, node.val);
    }

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

关键语句 return go(node.left, node.val); 详解:

Waifulabs Waifulabs

一键生成动漫二次元头像和插图

Waifulabs 317 查看详情 Waifulabs

理解 go 函数中 sum 参数的含义和返回值至关重要。

  • sum 参数的含义: go(node, sum) 中的 sum 参数代表的是在遍历到 node 之前,所有已经处理过的、且值大于或等于 node 原始值的节点之和。换句话说,它是“当前节点右侧(或更右侧)所有节点的累加和”。
  • node.val += go(node.right, sum);:
    • go(node.right, sum) 会递归地处理 node 的整个右子树。当这个调用返回时,它返回的 sum 是在处理完 node 的右子树中最右侧的节点后,累积到的总和。这个总和包含了 sum 参数的初始值以及 node 右子树中所有节点的值。
    • 将这个返回的 sum 加到 node.val 上,使得 node.val 更新为 node 的原始值加上所有比 node 大的节点值之和(包括右子树中的节点以及 sum 参数带来的更右侧的累加)。
  • return go(node.left, node.val);:
    • 此时,node.val 已经包含了 node 原始值以及所有比它大的节点值之和。这个更新后的 node.val 就是新的“累加和”,它将作为参数传递给 node 的左子树。
    • go(node.left, node.val) 会递归地处理 node 的整个左子树。左子树中的每个节点都会将这个 node.val 作为其初始累加和。
    • 最终,go(node.left, node.val) 会返回处理完整个左子树后,累积到的最终总和。这个总和包含了 node 及其右子树、node 自身以及 node 左子树中所有节点的值。
    • 这个返回值正是当前 go(node, sum) 函数需要向其调用者返回的值,以继续向上传递累积的总和。它确保了整个树的遍历过程中,sum 始终代表着当前节点及其右侧所有节点的累加值,正确地传递给下一个需要更新的节点。

示例解析

让我们使用提供的示例树来追踪 bstToGst 函数的执行过程:

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

初始调用:bstToGst(root_4) 会调用 go(root_4, 0)。

  1. go(root_4, 0):
    • 调用 go(root_4.right, 0),即 go(root_6, 0)。
  2. go(root_6, 0):
    • 调用 go(root_6.right, 0),即 go(root_7, 0)。
  3. go(root_7, 0):
    • 调用 go(root_7.right, 0),即 go(root_8, 0)。
  4. go(root_8, 0):
    • 调用 go(root_8.right, 0),即 go(null, 0) -> 返回 0。
    • root_8.val += 0 (8 + 0 = 8)。root_8.val 更新为 8。
    • 调用 go(root_8.left, 8),即 go(null, 8) -> 返回 8。
    • go(root_8, 0) 返回 8。
  5. go(root_7, 0) 收到 8:
    • root_7.val += 8 (7 + 8 = 15)。root_7.val 更新为 15。
    • 调用 go(root_7.left, 15),即 go(null, 15) -> 返回 15。
    • go(root_7, 0) 返回 15。
  6. go(root_6, 0) 收到 15:
    • root_6.val += 15 (6 + 15 = 21)。root_6.val 更新为 21。
    • 调用 go(root_6.left, 21),即 go(root_5, 21)。
  7. go(root_5, 21):
    • 调用 go(root_5.right, 21),即 go(null, 21) -> 返回 21。
    • root_5.val += 21 (5 + 21 = 26)。root_5.val 更新为 26。
    • 调用 go(root_5.left, 26),即 go(null, 26) -> 返回 26。
    • go(root_5, 21) 返回 26。
  8. go(root_6, 0) 收到 26:
    • go(root_6, 0) 返回 26。
  9. go(root_4, 0) 收到 26:
    • root_4.val += 26 (4 + 26 = 30)。root_4.val 更新为 30。
    • 调用 go(root_4.left, 30),即 go(root_1, 30)。
  10. go(root_1, 30):
    • 调用 go(root_1.right, 30),即 go(root_2, 30)。
  11. go(root_2, 30):
    • 调用 go(root_2.right, 30),即 go(root_3, 30)。
  12. go(root_3, 30):
    • 调用 go(root_3.right, 30),即 go(null, 30) -> 返回 30。
    • root_3.val += 30 (3 + 30 = 33)。root_3.val 更新为 33。
    • 调用 go(root_3.left, 33),即 go(null, 33) -> 返回 33。
    • go(root_3, 30) 返回 33。
  13. go(root_2, 30) 收到 33:
    • root_2.val += 33 (2 + 33 = 35)。root_2.val 更新为 35。
    • 调用 go(root_2.left, 35),即 go(null, 35) -> 返回 35。
    • go(root_2, 30) 返回 35。
  14. go(root_1, 30) 收到 35:
    • root_1.val += 35 (1 + 35 = 36)。root_1.val 更新为 36。
    • 调用 go(root_1.left, 36),即 go(root_0, 36)。
  15. go(root_0, 36):
    • 调用 go(root_0.right, 36),即 go(null, 36) -> 返回 36。
    • root_0.val += 36 (0 + 36 = 36)。root_0.val 更新为 36。
    • 调用 go(root_0.left, 36),即 go(null, 36) -> 返回 36。
    • go(root_0, 36) 返回 `36

以上就是深入理解 LeetCode 1038:利用反向中序遍历将二叉搜索树转换为累加树的详细内容,更多请关注其它相关文章!


# 是在  # seo技巧和seo思路  # SEO需要优化哪些东西  # 公司网站建设招标流程  # 机场网站建设流程图片  # seo网站取火 星  # zara营销推广活动的优劣  # seo蜘蛛的内容  # 德化县网站建设建议  # 内容营销推广员工作职责  # 南陵企业网站优化推广  # 并将  # 返回值  # javascript  # 这一  # 都比  # 于其  # 转换为  # 遍历  # 递归  # 子树  # 递归函数  # go  # node  # java 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 深入理解J*aScript Promise异步执行与微任务队列  QQ邮箱登录首页官网地址2026 QQ邮箱官方网页入口  AO3镜像入口大全 AO3网页版内容访问全集  网站内容防复制粘贴的实现策略与局限性  台积电1.4nm工艺A14瞄准2028:10年来性能提升80%  必由学官方平台入口 必由学在线课堂登录地址  如何在J*a中实现统一对象行为接口_项目大型化时的接口规范化  Shopware订单对象中获取产品自定义字段的正确方法  MAC怎么安装Homebrew包管理器_MAC为开发者和高级用户安装命令行工具  妖精动漫免费平台 妖精动漫官网资源观看网址  内存疯狂猛猛涨价:主板销量直接腰斩!  使用J*aScript检测输入元素是否包含在特定类中  优化HTML表单样式:解决输入框焦点跳动与元素间距问题  b站怎么取消点赞_b站点赞取消操作方法  Excel如何用迷你图显趋势_Excel用迷你图显趋势【趋势小图】  在Qt QML中通过Python字典动态更新TextEdit内容的教程  小红书怎么解除第三方平台绑定_小红书多平台登录解绑方法介绍  在Go开发中优雅管理ListenAndServe进程:GoSublime集成方案  微博网页版主页入口 微博官方网站免登录访问  MAC的“快捷指令”怎么同步到iPhone_MAC利用iCloud同步所有设备的自动化指令  Sublime怎么配置Nim语言环境_Sublime Nim代码高亮与补全  win11怎么查看应用耗电情况 Win11电池设置查看应用能耗排行榜【优化】  如何使用纯J*aScript判断Input元素是否在特定类容器内  解决 MongoDB 聚合查询中对象数组 _id 匹配问题  c++中的std::basic_string的SSO优化_c++短字符串优化深度解析  解决Bootstrap卡片顶部边距导致背景图下移的问题  深入理解字体排版:Adobe光学字偶距与CSS字偶距的差异与实现  ArrayList与LinkedList核心操作的Big-O复杂度分析  MAC如何安全彻底地删除文件_MAC使用终端命令确保文件无法被恢复  解决Tabulator日期时间排序问题的专业指南  Go语言中的*string:深入理解字符串指针  海棠电脑版入口_通过电脑访问海棠官网阅读  PS5 Pro有点优势但不多! 《燕云十六声》PS5平台与PC性能画面对比  126邮箱手机版登录官网2026_126手机邮箱免费入口最新  Promise错误处理:在catch后终止链式then执行的策略  在J*a中如何使用Exception包装底层异常_异常包装与信息传递方法说明  解决 Vaadin 8 中大文件音频播放与定位时出现的 IOException  PHP高效扁平化嵌套数组:使用array_merge与数组解包操作符  苹果手机如何防止被恶意App追踪  解决移动端滚动问题的overflow属性应用指南  优化 Jest 模拟:强制未实现函数抛出错误以提升测试效率  Win10如何清理注册表垃圾 Win10注册表维护与优化指南【慎用】  J*aScript类型检查_j*ascript代码规范  Lar*el Excel导入时生成自定义递增ID的策略与实践  CSS Flexbox与媒体查询:实现响应式布局中元素的并排与堆叠  《燕云十六声》两周内达九百万玩家!位居畅销榜第五  C++ map遍历方法大全_C++ map迭代器使用总结  ExcelARRAYTOTEXT函数怎么自定义分隔符输出数组文本_ARRAYTOTEXT实现动态生成SQL语句  4399网页游戏电脑版全新入口 4399电脑端在线玩指南  2026年发布! 美少女养成动作RPG《神剑少女战记》发布实机演示 

搜索