0

0

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

聖光之護

聖光之護

发布时间:2025-11-21 19:24:01

|

756人浏览过

|

来源于php中文网

原创

深入理解 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. 访问左子树: 最后访问左子树。左子树中的所有节点都比当前根节点小,但它们仍然需要加上之前累积的总和(包括根节点及其右子树的和)。

算法实现与代码解析

以下是实现这一转换的 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. 递归处理右子树:
        // 先处理右子树,并将右子树处理完后返回的累加和(即所有比当前节点大的节点值之和)
        // 加到当前节点的 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); 详解:

阿里云AI平台
阿里云AI平台

阿里云AI平台

下载

理解 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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

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

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

254

2023.09.22

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

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

1089

2024.03.01

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

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

500

2023.08.14

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

25

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

43

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

174

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

50

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

92

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

102

2026.03.06

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 6万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 3.4万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.6万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号