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

【LeetCode刷题日记】538.把二叉搜索树转换为累加树

🔥个人主页:北极的代码(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:苍穹外卖日记,SSM框架深入,JavaWeb
命运的结局尽可永在,不屈的挑战却不可须臾或缺!

前言:

大家好,我是代码不加冰,今天提前进入到我们的每日刷题环节,同时也是二叉树相关的最后一个题目,但是不知道这些题目够不够应对面试的,后面可能还会继续拓展一些题目,之后再更新一些模板解题思路,通用的,欢迎大家一起见证!

题目摘要:

538题要求将二叉搜索树转换为累加树,每个节点的新值等于原值加上所有大于该节点值的和。解题思路是利用反序中序遍历(右→根→左),维护累加和sum。递归法通过全局变量sum实现;迭代法借助栈模拟递归;Morris遍历则优化空间至O(1)。关键点:BST的中序性质与降序累加逻辑的巧妙结合。三种解法均保持O(n)时间复杂度,空间复杂度分别为O(h)、O(h)、O(1)。该题展现了二叉树遍历的灵活应用与空间优化技巧。

题目背景:538.把二叉搜索树转换为累加树

给出二叉搜索树的根节点root,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),将其转换为一个更大的树,使得原始二叉搜索树中的每个节点值都变为原本值加上原本二叉搜索树中所有比该节点值大的节点值的总和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键小于节点键的节点。
  • 节点的右子树仅包含键大于节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意:本题和 1038: 1038. 从二叉搜索树到更大和树 - 力扣(LeetCode) 相同

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]输出:[1,null,1]

示例 3:

输入:root = [1,0,2]输出:[3,3,2]

示例 4:

输入:root = [3,2,4,1]输出:[7,9,4,10]

提示:

  • 树中的节点数介于0104之间。
  • 每个节点的值介于-104104之间。
  • 树中的所有值互不相同
  • 给定的树为二叉搜索树。

题目解析:


刚拿到这个题目,我们先看看题目要求,题目说的很明白,将二叉搜索树转换成累加树,二叉搜索树自然不用多说,前面我们已经介绍了很多,也足够了解了

至于累加树,我们自己不知道,但是题目给我们说明了,累加树的意思就是将原本二叉搜索树的每个节点进行相应的累加,累加树每个节点的值=原本二叉搜索树的节点的值+比原来节点值大的节点的总和。

那我们该怎么累加呢,该怎么找到比该节点值大的值呢,首先由于是二叉搜索树,分辨节点值的大小其实很简单,那累加的操作呢,看着似乎很麻烦

其实这就是一棵树,大家可能看起来有点别扭,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13],是不是感觉这就简单了。

为什么变成数组就是感觉简单了

因为数组大家都知道怎么遍历,从后向前,挨个累加就可以了,这换成了二叉搜索树,看起来就别扭了一些。

BST 的中序遍历是升序序列(左→根→右)

累加树的要求:每个节点的新值 = 所有 ≥ 该节点值的节点值之和

换句话说,如果我们按照降序(右→根→左)遍历 BST:

  • 遍历到的第一个节点(最大值)→ 累加和 = 它本身

  • 第二个节点 → 累加和 = 它本身 + 上一个节点的累加和

  • 第三个节点 → 累加和 = 它本身 + 前面所有节点的累加和

结论:使用反序中序遍历(右→根→左),维护一个累加和sum,每访问一个节点就更新它的值。


整体的逻辑我们分析的差不多了,下面我们来看看具体的实现:

  1. 初始化全局变量sum = 0,用于记录已经遍历过的节点值之和

  2. 递归遍历:先遍历右子树 → 处理当前节点 → 再遍历左子树

  3. 处理当前节点时:

    • sum += root.val

    • root.val = sum

  4. 返回根节点


递归法图解示例
输入:root = [4,1,6,0,2,5,7,null,null,null,3,null,null,null,8] text 4 / \ 1 6 / \ / \ 0 2 5 7 \ \ 3 8

反序中序遍历顺序(右→根→左)

步骤访问节点当前 sum(累加前)新值计算新值
180sum = 0+88
278sum = 8+715
3615sum = 15+621
4521sum = 21+526
5426sum = 26+430
6330sum = 30+333
7233sum = 33+235
8135sum = 35+136
9036sum = 36+036

转换后的树

text 30 / \ 36 21 / \ / \ 36 35 26 15 \ \ 33 8 验证:对于节点 4,原始 ≥4 的节点有 {4,5,6,7,8},和 = 4+5+6+7+8 = 30 ✅

迭代法的具体流程:
示例树 text 4 / \ 1 6 / \ / \ 0 2 5 7 \ \ 3 8示例树

反序中序遍历顺序(右→根→左):

8 → 7 → 6 → 5 → 4 → 3 → 2 → 1 → 0

循环职责处理对象
内层 while压栈当前节点 + 所有右子节点
外层 while控制整体流程 + 转向左子树当右子树处理完后,通过cur = cur.left去处理左子树

详细解释

java while (cur != null || !stack.isEmpty()) { // 内层while:只负责把"当前节点及所有右子节点"压栈 while (cur != null) { stack.push(cur); cur = cur.right; // 一直往右走 } // 弹出栈顶(处理当前节点) cur = stack.pop(); sum += cur.val; cur.val = sum; // 外层while负责:转向左子树 cur = cur.left; // ← 这里! }

图解分工

以节点4为例:

text 4 ← cur 在这里 / \ 1 6

内层 while 做的事:

text cur=4 → 压入4 → cur=6 cur=6 → 压入6 → cur=7 cur=7 → 压入7 → cur=8 cur=8 → 压入8 → cur=null

结果:stack = [4,6,7,8](右链全部入栈)

外层 while 做的事:

text 弹出8 → 处理8 → cur = 8.left = null 弹出7 → 处理7 → cur = 7.left = null 弹出6 → 处理6 → cur = 6.left = 5 ← 转向左子树!

关键:当处理完节点6后,cur = 5,下一轮循环的内层while又会把节点5及它的右子节点(5.right=null)压栈。


完整流程图

text 初始: cur=4 │ ▼ ┌─────────────────────────────────┐ │ 内层while: 压入4→6→7→8 │ │ stack=[4,6,7,8], cur=null │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 弹出8, 处理8, cur=8.left=null │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 弹出7, 处理7, cur=7.left=null │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 弹出6, 处理6, cur=6.left=5 │ ← 转向左子树 └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 内层while: 压入5, cur=5.right=null│ │ stack=[4,5], cur=null │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 弹出5, 处理5, cur=5.left=null │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 弹出4, 处理4, cur=4.left=1 │ ← 转向左子树 └─────────────────────────────────┘ │ ▼ 继续处理左子树...

总结

循环层次作用类比
内层 while深度优先往右走,记录路径"一直向右到底"
外层 while控制整体遍历顺序,处理完右子树后转向左"处理完右边,再处理左边"

一句话记忆

  • 内层 while = 压右链

  • cur = cur.left = 转左子树

两者配合,完美实现右→根→左的遍历顺序。


题目答案:

递归法:
class Solution { private int sum = 0; // 累加和 public TreeNode convertBST(TreeNode root) { if (root == null) { return null; } // 先遍历右子树(处理更大的值) convertBST(root.right); // 处理当前节点 sum += root.val; root.val = sum; // 再遍历左子树(处理更小的值) convertBST(root.left); return root; } }
迭代法(使用栈)
java class Solution { public TreeNode convertBST(TreeNode root) { if (root == null) return null; Stack<TreeNode> stack = new Stack<>(); TreeNode cur = root; int sum = 0; // 反序中序遍历:先遍历右子树 while (cur != null || !stack.isEmpty()) { // 将右子树全部入栈 while (cur != null) { stack.push(cur); cur = cur.right; } // 处理当前节点 cur = stack.pop(); sum += cur.val; cur.val = sum; // 转向左子树 cur = cur.left; } return root; } }

Morris 遍历(空间 O(1))
java class Solution { public TreeNode convertBST(TreeNode root) { int sum = 0; TreeNode cur = root; while (cur != null) { // 如果有右子树,找到右子树的最左节点 if (cur.right != null) { TreeNode prev = cur.right; while (prev.left != null && prev.left != cur) { prev = prev.left; } if (prev.left == null) { // 建立线索 prev.left = cur; cur = cur.right; } else { // 第二次访问当前节点 prev.left = null; // 断开线索 sum += cur.val; cur.val = sum; cur = cur.left; } } else { // 没有右子树,直接处理当前节点 sum += cur.val; cur.val = sum; cur = cur.left; } } return root; } }

复杂度分析
方法时间复杂度空间复杂度
递归O(n)O(h),h 为树高(最坏 O(n))
迭代(栈)O(n)O(h)
MorrisO(n)O(1)

结语:

如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!

http://www.jsqmd.com/news/923433/

相关文章:

  • LinkSwift网盘直链下载助手:八大主流网盘高速下载终极指南
  • 大模型求职必看:收藏这份分层准备指南,从新手到大厂Offer收割机
  • FPGA加速Transformer与VLM视觉任务的优化实践
  • 国信中业—原位XPS(In-situ XPS)将“反应”和“测试”同步进行
  • AnimateDiff动画生成指南:5分钟从静态图像到动态视频的完整教程
  • 工业云脑:11 未来:6G、卫星、量子加密
  • Δ-Motif算法:GPU并行化子图同构匹配技术解析
  • Windows 11终极优化指南:如何用Win11Debloat一键清理系统垃圾和提升性能
  • Layerdivider快速入门指南:免费AI智能分层工具3分钟生成PSD文件
  • OpCore-Simplify:告别黑苹果配置噩梦,30分钟搞定专业级EFI配置
  • 不同场景下电动挡烟垂壁怎么选
  • LanzouAPI技术揭秘:如何通过PHP实现蓝奏云直链解析的高效方案
  • PHP遇到报错,不只搜解决方案,要看 堆栈跟踪,读 源码。
  • 2026梧州市防水补漏公司权威推荐:卫生间、阳台、屋顶、地下室、飘窗、外墙漏水,专业防水公司TOP5口碑榜+全维度测评(2026年6月最新深度行业资讯) - 防水百科
  • DQC1量子计算模型与迹估计技术详解
  • .NET Windows Desktop Runtime:彻底解决Windows桌面应用部署难题的终极指南
  • 大模型应用层开发学习路径:从传统后端到AI高薪岗位,收藏这份进阶指南!
  • 零基础从零到一PHP打断点的庖丁解牛
  • 原位红外(in situ FTIR)光谱:从技术突破到反应机理研究
  • 终极指南:如何用TradingAgents-CN搭建AI股票分析平台
  • WarcraftHelper:魔兽争霸3现代电脑完美运行终极指南
  • 杭州余杭永鸿再生资源:余杭区废旧金属回收公司 - LYL仔仔
  • 2026肇庆市防水补漏公司权威推荐:卫生间、阳台、屋顶、地下室、飘窗、外墙漏水,专业防水公司TOP5口碑榜+全维度测评(2026年6月最新深度行业资讯) - 防水百科
  • 3秒隐形:Boss-Key如何让你的数字生活拥有“第二空间”
  • 5分钟掌握StreamFX:从直播小白到专业主播的蜕变之路
  • 如何用TripoSR在0.5秒内完成高质量3D建模?终极快速单图像3D重建完全指南
  • QMCDecode:重新掌控你的音乐收藏,告别QQ音乐加密限制
  • 2026西安瓷砖脱落维修机构TOP4:靠谱修缮团队推荐 专业瓷砖空鼓维修公司排名推荐(2026年5月瓷砖空鼓维修最新TOP权威排名) - 冠盾建筑修缮
  • PHP的打断点就是手动var_dump+exit?
  • GlosSI终极指南:在Windows上实现系统级Steam控制器支持