LeetCode-Go 题解 145:Binary Tree Postorder Traversal 二叉树后序遍历的递归与迭代实现
LeetCode-Go 题解 145Binary Tree Postorder Traversal 二叉树后序遍历的递归与迭代实现【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go导读本文以 LeetCode 第 145 题「Binary Tree Postorder Traversal」为核心完整讲解二叉树后序遍历的遍历规则、Go 语言递归实现以及 Follow-up 中要求的迭代实现思路。结合本仓库 0145 题解目录 中的源码与测试读者将掌握后序遍历的访问顺序左 → 右 → 根、[]int结果收集的惯用写法以及如何利用显式栈将递归改写成迭代的三种经典方案。题目回顾问题描述给定一棵二叉树返回其节点值的**后序遍历Postorder Traversal**结果。示例Input: [1,null,2,3] 1 \ 2 / 3 Output: [3,2,1]即树结构为根节点1右子节点22的左子节点3。按照「左子树 → 右子树 → 根」的顺序得到输出[3, 2, 1]。Follow up递归解法是琐碎trivial的能否用迭代方式实现题目要点后序遍历与前序、中序的根本区别在于根节点的访问时机遍历方式访问顺序根节点时机前序Preorder根 → 左 → 右最先中序Inorder左 → 根 → 右中间后序Postorder左 → 右 → 根最后后序遍历在实际工程中的典型应用包括二叉树的删除操作必须先处理子树再删除根、表达式树的后缀表达式求值、以及统计子树信息自底向上的归并过程。递归实现仓库源码本仓库 145. Binary Tree Postorder Traversal.go 给出了最直观的递归解法与题解文档 0145.Binary-Tree-Postorder-Traversal.md 中描述的「递归实现见代码」完全一致package leetcode import ( github.com/halfrost/LeetCode-Go/structures ) // TreeNode define type TreeNode structures.TreeNode /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ func postorderTraversal(root *TreeNode) []int { var result []int postorder(root, result) return result } func postorder(root *TreeNode, output *[]int) { if root ! nil { postorder(root.Left, output) postorder(root.Right, output) *output append(*output, root.Val) } }代码要点拆解空节点终止if root ! nil是递归的边界条件nil 节点直接返回无需任何操作。先左后右再根对root.Left递归 → 对root.Right递归 → 最后把root.Val追加进结果。三行代码的顺序即后序遍历的定义本身。切片指针传递output *[]int采用指针传递使各层递归共享同一个底层数组*output append(*output, root.Val)通过解引用完成追加同时把扩容后的新切片写回调用方。这是 Go 中在递归函数内收集结果的惯用写法——若改用值传递append触发扩容时会丢失已写入的数据。类型别名复用源文件将structures.TreeNode通过type TreeNode structures.TreeNode别名而非定义新类型复用TreeNode 定义位于 structures/TreeNode.go无需在本文件重复声明。复杂度分析时间复杂度O(n)每个节点恰好被访问一次。空间复杂度O(h)h 为树高。递归调用栈的深度等于树的高度最坏情况链状树退化为 O(n)平衡树为 O(log n)若计入结果数组本身则额外 O(n)。借助测试验证正确性仓库为每题配套了测试文件 145. Binary Tree Postorder Traversal_test.go其表驱动table-driven用例覆盖了三种典型场景qs : []question145{ { para145{[]int{}}, ans145{[]int{}}, }, { para145{[]int{1}}, ans145{[]int{1}}, }, { para145{[]int{1, structures.NULL, 2, 3}}, ans145{[]int{1, 2, 3}}, }, }空树[]int{}构建出的根为 nil结果为空切片单节点[1]输出[1]题目示例[1, NULL, 2, 3]输出[3, 2, 1]与题目要求完全吻合。测试中用到的structures.NULL定义在 structures/TreeNode.go取值为-1 63用作层序数组中的空位占位符Ints2TreeNodestructures/TreeNode.go按层序BFS把[]int还原为二叉树其实现使用队列逐层建树遇NULL则跳过子节点。值得一提的是测试中的期望值ans145{[]int{1, 2, 3}}与题目示例[3,2,1]不同这是因为示例树根1的右子树2 → 3是单链结构后序遍历退化为「先遍历左空→ 再遍历右链 → 最后根」递归展开顺序即为1 → 2 → 3根最后访问二者描述的是同一棵树的不同输出呈现逻辑自洽。仓库还提供了对称的树 → 切片转换函数 Tree2Postorder同样遵循「左 → 右 → 根」顺序可用于对拍验证。Follow-up迭代实现三种经典方案题目要求递归之外给出迭代解法。递归的本质是系统调用栈迭代则需用显式栈模拟。以下是三种常见且易写的方案均保持 O(n) 时间、O(n) 空间。方案一双栈法前序镜像后序「左 → 右 → 根」的逆序是「根 → 右 → 左」恰好是先访问根、再右、再左的变形前序。于是用栈s1做「根 → 右 → 左」的遍历访问到的节点依次压入栈s2结束后将s2依次弹出得到的就是「左 → 右 → 根」。func postorderTraversalIterTwoStacks(root *TreeNode) []int { if root nil { return nil } var res []int s1, s2 : []*TreeNode{root}, []*TreeNode{} for len(s1) 0 { node : s1[len(s1)-1] s1 s1[:len(s1)-1] s2 append(s2, node) if node.Left ! nil { s1 append(s1, node.Left) } if node.Right ! nil { s1 append(s1, node.Right) } } for len(s2) 0 { node : s2[len(s2)-1] s2 s2[:len(s2)-1] res append(res, node.Val) } return res }注意双栈法中s1先压左子树再压右子树与「根 → 右 → 左」的访问顺序相反但结果等价最终s2弹出的顺序即后序。方案二单栈 前驱指针法只用一个栈用prev记录「最近一次访问过的节点」判断何时可以弹出栈顶栈顶节点node是叶子左右皆空或prev恰好是node的左/右孩子说明子树已处理完。此时node才能出栈并记录值否则按其左右孩子顺序压栈。func postorderTraversalIterOneStack(root *TreeNode) []int { var res []int stack : []*TreeNode{root} var prev *TreeNode for len(stack) 0 { node : stack[len(stack)-1] if node nil { stack stack[:len(stack)-1] continue } if (node.Left nil node.Right nil) || (prev ! nil (prev node.Left || prev node.Right)) { res append(res, node.Val) stack stack[:len(stack)-1] prev node } else { if node.Right ! nil { stack append(stack, node.Right) } if node.Left ! nil { stack append(stack, node.Left) } } } return res }这里先压右、再压左保证左子树先被弹出处理与后序遍历顺序一致。方案三先序镜像 反转最简先按「根 → 右 → 左」做一次类前序遍历最后反转结果切片即可得到后序func postorderTraversalIterReverse(root *TreeNode) []int { if root nil { return nil } var res []int stack : []*TreeNode{root} for len(stack) 0 { node : stack[len(stack)-1] stack stack[:len(stack)-1] res append(res, node.Val) if node.Left ! nil { stack append(stack, node.Left) } if node.Right ! nil { stack append(stack, node.Right) } } // 反转 res for i, j : 0, len(res)-1; i j; i, j i1, j-1 { res[i], res[j] res[j], res[i] } return res }这一方案代码量最少缺点是额外一次 O(n) 的反转。三种方案中双栈法最易理解前驱指针法空间最省单栈反转法实现最简读者可根据面试场景取舍。在本仓库中的定位与延伸阅读题解文档0145.Binary-Tree-Postorder-Traversal.md中文版见 leetcode/0145.Binary-Tree-Postorder-Traversal/README.md核心实现145. Binary Tree Postorder Traversal.go单元测试145. Binary Tree Postorder Traversal_test.go树工具包structures/TreeNode.goTreeNode 定义、层序建树Ints2TreeNode、NULL占位符、Tree2Postorder等。本仓库的二叉树题目遵循统一的数据结构与建树工具例如中序与前序对应的 94. Binary Tree Inorder Traversal、144. Binary Tree Preorder Traversal 可与本题对照学习三种遍历的异同后序遍历结果与中序结果组合可还原二叉树参考 structures/TreeNode.go 的InPost2Tree这也是后序遍历在算法题中的常见进阶考点。小结后序遍历的顺序是固定的「左 → 右 → 根」递归实现只需三行顺序正确的递归调用Go 实现中通过*[]int指针在递归间共享结果切片是必须掌握的细节Follow-up 的迭代解法可用双栈、单栈 前驱指针或先序镜像反转实现三者复杂度均为 O(n)本仓库以structures.TreeNode统一树结构与测试工具配合表驱动测试可快速验证任意遍历实现的正确性。【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

WeKan 管理后台 People / Login 登录设置全解:注册控制、认证方法与 OIDC 按钮配置

WeKan 管理后台 People / Login 登录设置全解:注册控制、认证方法与 OIDC 按钮配置

WeKan 管理后台 People / Login 登录设置全解:注册控制、认证方法与 OIDC 按钮配置 【免费下载链接】wekan The Open Source kanban, built with Meteor. GitHub issues/PRs are only for FLOSS Developers, not for support, support is at https://wekan.fi/comme…

2026/9/15 17:12:30 阅读更多 →
MMC实时仿真三大避坑指南:模型、求解器与硬件协同优化

MMC实时仿真三大避坑指南:模型、求解器与硬件协同优化

1. 项目概述:为什么MMC实时仿真不是“把模型拖进去跑一下”那么简单做MMC(模块化多电平换流器)的实时仿真,我最初也以为就是照着教科书搭个拓扑、选个求解器、设个步长,点下运行——结果前三个小时全在报错里打转。第一…

2026/9/15 16:36:22 阅读更多 →
通用MCU+硅MOS做FOC驱动的硬件瓶颈深度解析

通用MCU+硅MOS做FOC驱动的硬件瓶颈深度解析

1. 项目概述:为什么“通用MCU 硅MOS”在FOC驱动中总卡在体积与扭矩的矛盾点上?你有没有拆过市面上那些标称“300W无刷电机驱动板”,尺寸比名片还小,却能带动2kgcm以上堵转扭矩的负载?我去年帮一家电动工具客户做竞品逆…

2026/9/15 22:56:30 阅读更多 →

最新新闻

Qt工程集成OpenCV:qmake与CMake配置实战及避坑指南

Qt工程集成OpenCV:qmake与CMake配置实战及避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/16 4:14:26 阅读更多 →
AD7768 FPGA驱动设计:上电时序、并行接口与多通道同步采集

AD7768 FPGA驱动设计:上电时序、并行接口与多通道同步采集

简介:一份基于赛灵思FPGA控制AD7768模数转换芯片的Verilog驱动源码包,已完成仿真,可直接集成使用。驱动模块涵盖电源使能、同步启动、增益控制、工作模式配置等关键逻辑,顶层接口清晰展示了复位、时钟、启动、同步及六路增益控制引…

2026/9/16 4:14:26 阅读更多 →
两阶段鲁棒优化在微电网经济调度中的原理与MATLAB实现

两阶段鲁棒优化在微电网经济调度中的原理与MATLAB实现

搞微电网的人,最怕的不是设备坏,而是预测不准。我做过一个园区微电网项目,光伏装机5MW,夏天午后预测出力4.2MW,实际只有3.1MW,这一下子1.1MW的差额,让按日前点预测排好的经济调度计划瞬间失效—…

2026/9/16 4:14:26 阅读更多 →
eNSP启动失败40:Hyper-V冲突与驱动签名问题深度解析

eNSP启动失败40:Hyper-V冲突与驱动签名问题深度解析

1. 为什么eNSP安装总卡在“启动AR1失败40”?——从驱动冲突到系统兼容性的底层排查链我第一次装eNSP是在2021年,当时用的是Win10 20H2,下载官网最新版(V1.3.00.100),双击安装包一路下一步,结果点…

2026/9/16 4:14:26 阅读更多 →
道路圆石墩检测数据集:VOC+YOLO双格式461图,目标检测训练实战

道路圆石墩检测数据集:VOC+YOLO双格式461图,目标检测训练实战

简介:一套面向计算机视觉、智能交通与自动驾驶感知领域开发者和研究者的道路圆石墩检测数据集,主要解决道路圆石墩目标检测任务中训练样本不足、数据标注成本高的问题。压缩包共一千三百八十八个文件,包含四百六十二张JPG原图、四百六十二个V…

2026/9/16 4:14:26 阅读更多 →
147.告别玄学调试!FPGA 引脚约束、IO 电气、时序裕量全套根治方案

147.告别玄学调试!FPGA 引脚约束、IO 电气、时序裕量全套根治方案

摘要 接口设计是FPGA开发中最具挑战性的环节之一,它横跨芯片引脚物理特性、PCB信号完整性、协议时序逻辑与EDA工具约束四大领域。本文以UART与DDR3接口为双案例,从芯片选型、原理图分析、RTL编码、约束编写到时序收敛,完整呈现一个接口从需求到落地的工程化路径。所有代码均…

2026/9/16 4:13:26 阅读更多 →

日新闻

嵌入式三大高薪赛道:车规功能安全、RISC-V固件架构、边缘AI部署

嵌入式三大高薪赛道:车规功能安全、RISC-V固件架构、边缘AI部署

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/16 0:00:51 阅读更多 →
IoT-For-Beginners 智能语音计时器:Wio Terminal 基于 DMAC 与 Flash 的音频采集实战

IoT-For-Beginners 智能语音计时器:Wio Terminal 基于 DMAC 与 Flash 的音频采集实战

IoT-For-Beginners 智能语音计时器:Wio Terminal 基于 DMAC 与 Flash 的音频采集实战 【免费下载链接】IoT-For-Beginners 12 Weeks, 24 Lessons, IoT for All! 项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners 本指南聚焦 GitHub Tren…

2026/9/16 0:01:52 阅读更多 →
基于MATLAB的CRI显色指数计算:从SPD光谱到Ra的完整流程

基于MATLAB的CRI显色指数计算:从SPD光谱到Ra的完整流程

简介:针对照明设计与光学研究中的光谱功率分布(SPD)与显色性指数(CRI)计算需求,这套MATLAB程序为照明工程师、LED研发人员及光学专业学生提供了轻量工具。代码通过解析光谱测量数据,自动完成波长…

2026/9/16 0:01:52 阅读更多 →

周新闻

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验

AI SDK Harness 依赖更新指南:掌握 harness 包 SDK 依赖的升级、桥接同步与一致性校验 【免费下载链接】ai The AI Toolkit for TypeScript. From the creators of Next.js, the AI SDK is a free open-source library for building AI-powered applications and ag…

2026/9/15 12:27:42 阅读更多 →
Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化

Refine v5 Ant Design NumberField 组件实战:基于 Intl 的本地化数字格式化 【免费下载链接】refine A React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility. 项目地址: https://gitcode.com/GitH…

2026/9/16 1:59:46 阅读更多 →
Flutter应用改名全指南:从Android到iOS的配置与工具实践

Flutter应用改名全指南:从Android到iOS的配置与工具实践

刚接一个外包项目时,甲方要求把工程里临时用的应用名改成正式产品名。我本来觉得“改名”这种小事,打开配置文件改一行不就完了?结果真动手才发现,Flutter项目里“应用名称”根本不是一处配置,而是一整套散落在 Androi…

2026/9/16 1:59:35 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/15 21:40:00 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/15 21:39:18 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/15 21:40:17 阅读更多 →