当前位置: 首页 > news >正文

leetcode538.把二叉搜索树转换为累加树:反向中序遍历的数值累加之道

一、题目深度解析与BST特性利用

题目描述

给定一棵二叉搜索树(BST),将其转换为累加树。累加树的定义为:每个节点的值变为原树中大于或等于该节点值的所有节点值之和。例如,原BST中节点值为5,转换后该节点值应为原树中所有大于等于5的节点值相加的结果。要求通过递归方式实现转换,且需保持树的结构不变。

BST核心特性的应用

二叉搜索树的重要性质是左子树所有节点值 < 根节点值 < 右子树所有节点值,其中序遍历结果为严格递增序列。基于此特性,我们可以发现:若按反向中序遍历(右-根-左)的顺序访问节点,每次访问到一个节点时,已遍历过的节点值均大于当前节点值,此时将已遍历节点的累计和加上当前节点值,就能得到转换后的新值,这正是解决本题的关键思路。

二、递归解法的核心实现与逻辑框架

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {int sum;  // 用于记录累计和public TreeNode convertBST(TreeNode root) {sum = 0;  // 初始化累计和为0sumTree(root);  // 开始递归转换return root;  // 返回转换后的根节点}public void sumTree(TreeNode root){if(root == null){return;  // 递归终止条件:遇到空节点返回}sumTree(root.right);  // 先递归处理右子树sum += root.val;  // 将当前节点值加入累计和root.val = sum;  // 更新当前节点值为累计和sumTree(root.left);  // 再递归处理左子树}
}

核心设计解析

  1. 全局变量sum
    • 用于记录从右到左遍历过程中,已访问节点的数值总和。初始化为0,随着遍历的进行,不断累加节点值。
  2. 递归入口与返回
    • convertBST方法作为对外接口,初始化sum后调用sumTree方法启动递归转换,并最终返回转换后的根节点,方便外部获取整棵累加树。
  3. 递归终止条件
    • if(root == null) return;:当遇到空节点时,直接返回,结束当前递归分支,避免空指针异常。
  4. 反向中序遍历逻辑
    • sumTree(root.right);:先递归处理当前节点的右子树,确保在处理当前节点前,已将所有大于当前节点值的节点累计完毕。
    • sum += root.val;:将当前节点值加入累计和sum
    • root.val = sum;:更新当前节点值为累计和,完成节点值的转换。
    • sumTree(root.left);:最后递归处理当前节点的左子树,继续对左子树的节点进行转换。

三、核心问题解析:遍历顺序与数值累加逻辑

1. 反向中序遍历的必要性

普通中序遍历(左-根-右)访问节点的顺序是从小到大,而本题需要将每个节点值更新为大于等于它的所有节点值之和。若采用普通中序遍历,在访问到某个节点时,还未遍历到所有大于它的节点,无法直接计算出目标累加值。

而反向中序遍历(右-根-左)能保证:每次访问到一个节点时,所有大于该节点值的节点均已被访问并累加,此时将累计和加上当前节点值,即可得到转换后的新值。例如,对于BST中的某个节点,其右子树的所有节点值均大于它,通过先递归处理右子树,能提前完成右子树节点值的累加,再处理当前节点时就能正确更新其值。

2. 数值累加与节点更新逻辑

在递归过程中,sum变量扮演着核心角色:

  • 右子树递归sumTree(root.right)递归调用会不断深入右子树,将右子树中所有节点值逐步累加到sum中。
  • 当前节点处理
    • sum += root.val;将当前节点值加入累计和sum,此时sum即为大于等于当前节点值的所有节点值之和。
    • root.val = sum;直接将sum赋值给当前节点,完成节点值的更新。
  • 左子树递归sumTree(root.left)处理左子树时,sum已包含了当前节点及其右子树所有节点值的累加和,继续对左子树节点进行转换,保证整棵树的节点都被正确更新。

3. 递归过程中的状态传递

sum作为全局变量,在递归调用过程中保持状态的连续性。每一层递归调用结束后,sum的值会被传递到上一层,影响上层节点的计算。例如,父节点在处理时,sum已经包含了其右子树所有节点的累加和,在此基础上加上父节点自身的值,就能得到父节点转换后的正确值,实现了从下往上的累计和传递与节点更新。

四、递归流程深度模拟:以示例BST展示转换过程

假设给定如下BST:

      4/ \1   6/   / \0   5   7\8
  1. 首次递归调用
    • 从根节点4开始,调用sumTree(root),先执行sumTree(root.right),即递归处理右子树6
  2. 右子树6的递归
    • 对于节点6,继续执行sumTree(root.right),递归处理其右子树7
    • 对于节点7,其右子树为空,返回后执行sum += root.val;,此时sum = 0 + 7 = 7,再执行root.val = sum;,节点7的值更新为7
    • 接着执行sumTree(root.left)7的左子树为空,返回。
    • 回到节点6,此时sum = 7,执行sum += root.val;sum = 7 + 6 = 13root.val = sum;,节点6的值更新为13
    • 然后执行sumTree(root.left),递归处理6的左子树5
    • 对于节点5,其右子树8,递归处理88的左右子树为空,sum = 13 + 8 = 218的值更新为21,返回后5的值更新为21
  3. 回到根节点4
    • 右子树处理完毕,此时sum = 21,执行sum += root.val;sum = 21 + 4 = 25root.val = sum;,根节点4的值更新为25
  4. 处理左子树1
    • 递归处理1,先处理其右子树(为空),然后sum = 25 + 1 = 261的值更新为26
    • 再处理1的左子树0sum = 26 + 0 = 260的值更新为26

最终转换后的累加树如下:

      25/ \26  32/   / \26   32  39\39

五、算法复杂度分析

1. 时间复杂度

递归过程中,每个节点都会被访问且仅访问一次,对每个节点的操作(更新值和累加)都是常数级别的。因此,时间复杂度为O(n),其中n为二叉树的节点数。无论树的结构如何,都需要遍历每一个节点完成转换。

2. 空间复杂度

算法的空间复杂度主要取决于递归调用栈的深度。在最坏情况下(树退化为链表),递归栈深度为O(n);而在平衡二叉树中,递归栈深度为O(log n)。因此,空间复杂度为O(h),其中h为树的高度。此外,代码中使用的sum变量仅占用常数级空间,对整体空间复杂度影响较小。

六、核心技术点总结:累加树转换的关键要素

  1. 反向中序遍历策略:利用BST的有序性,通过反向中序遍历,保证在处理每个节点时,能获取到所有大于等于该节点值的累计和,是实现转换的核心思路。
  2. 全局变量状态传递:借助全局变量sum记录累计和,在递归调用过程中传递状态,确保每个节点的转换都基于正确的累计值。
  3. 递归终止与遍历逻辑:严谨的递归终止条件(空节点返回)和清晰的遍历顺序(右-根-左),保证了整棵树的每个节点都能被正确转换,且不破坏树的结构。

七、常见误区与优化建议

1. 错误的遍历顺序选择

  • 误区:采用普通中序遍历(左-根-右)或前序、后序遍历,导致在处理节点时无法获取到所有大于等于该节点值的累计和,从而无法正确转换节点值。
  • 正确做法:始终坚持反向中序遍历,确保遍历顺序符合累加树的构建逻辑。

2. 忽略全局变量的初始化

  • 误区:未在每次调用convertBST方法时将sum初始化为0,导致多次转换时累计和错误,影响结果准确性。
  • 正确做法:在convertBST方法中明确对sum进行初始化,保证每次转换的独立性和正确性。

3. 优化建议:迭代实现

如果担心递归深度过深导致栈溢出问题,可以考虑使用迭代方式,借助栈数据结构模拟递归过程。通过手动管理节点的访问顺序,同样按照反向中序遍历的逻辑进行节点值的转换,将空间复杂度优化为O(n)(在最坏情况下,栈中需存储所有节点),同时避免递归栈的潜在风险,提升算法在大规模数据下的稳定性。

通过对本题递归解法的详细剖析,我们深入理解了如何利用二叉搜索树的特性和反向中序遍历,高效实现向累加树的转换。这种思路不仅在算法问题中具有典型意义,在实际的数据处理和转换场景中,对于处理有序数据的累计计算也有着重要的参考价值。

相关文章:

  • leetcode108.将有序数组转换为二叉搜索树:递归切分中点构建平衡树的智慧
  • 第10讲、Odoo 18框架设计原理全解析
  • PyTorch中nn.Module详解
  • 西瓜书第九章——集成学习
  • 服务器如何配置防火墙管理端口访问?
  • GitHub 趋势日报 (2025年05月29日)
  • Java类和对象详解
  • 【代码训练营Day03】链表part1
  • 4.2.2 Spark SQL 默认数据源
  • 一文详谈Linux中的时间管理和定时器编程
  • 【计算机网络】传输层UDP协议
  • Hive的JOIN操作如何优化?
  • Electron-vite【实战】MD 编辑器 -- 系统菜单(含菜单封装,新建文件,打开文件,打开文件夹,保存文件,退出系统)
  • 软件测评中心如何确保软件品质?需求分析与测试计划很关键
  • Linux研学-环境搭建
  • .NET 查找 DLL 的路径顺序
  • Netty 实战篇:为 Netty RPC 框架引入调用链追踪,实现链路透明化
  • 基于原生JavaScript前端和 Flask 后端的Todo 应用
  • YOLOv8目标检测实战-(TensorRT原生API搭建网络和使用Parser搭建网络)
  • DeepSeek-R1-0528-Qwen3-8B 本地ollama离线运行使用和llamafactory lora微调
  • 接工程平台/商丘seo教程
  • 济宁建筑人才网/seo整站优化费用
  • 专业仿站网站建设/惠州seo关键词推广
  • 建设网站要服务器吗/西安官网seo公司
  • 涟水做网站/磁力兔子搜索引擎
  • 搭建单位网站/抖音推广运营