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

【LeetCode每日一题】94. 二叉树的中序遍历 104. 二叉树的最大深度

每日一题:二叉树

  • 94. 二叉树的中序遍历
    • 题目
    • 总体思路
    • 代码
    • 基础知识
  • 104. 二叉树的最大深度
    • 题目
    • 总体思路
    • 代码

2025.9.4

94. 二叉树的中序遍历

题目

给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。

示例 1:
在这里插入图片描述
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]

提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

总体思路

迭代:栈模拟
中序遍历顺序:Left → Node → Right。
用栈来模拟递归:

  1. 用指针 cur 从根开始,一路把所有左孩子入栈,直到没有左孩子(cur == nil)。
  2. 弹栈得到当前节点 node,把它的值加入结果。
  3. 让 cur 指向 node.Right,然后继续 1~2 的循环。
  4. 终止条件:cur == nil 且 栈空。
    时间复杂度 O(n),空间复杂度最坏 O(h)(树高,极端是 O(n))

递归实现
采用递归(Recursive)的方式:

如果节点为空,直接返回。
否则按照 左 → 根 → 右 的顺序访问节点,把结果存到切片 res 中。

代码

golang
迭代:栈模拟

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int   // Val 存储节点的值(int 类型)*     Left *TreeNode   // Left 指向左子节点。*     Right *TreeNode   // Right 指向右子节点。* }*/
// 无注释纯享:
func inorderTraversal(root *TreeNode) []int {res := make([]int, 0)stack := make([]*TreeNode, 0)cur := rootfor cur !=nil || len(stack) > 0 {for cur != nil {stack = append(stack,cur)cur=cur.Left}top := stack[len(stack)-1]stack = stack[:len(stack)-1]res = append (res, top.Val)cur = top.Right}return res
}// 迭代:栈模拟
// 中序遍历(迭代版,使用切片充当栈)
func inorderTraversal(root *TreeNode) []int {// 结果切片:存遍历顺序res := make([]int, 0)// 用切片当栈,元素类型是 *TreeNodestack := make([]*TreeNode, 0)// cur 指向当前走到的节点cur := root// Go 没有 while,用 for 当 while:条件为“当前节点非空 或 栈非空”for cur != nil || len(stack) > 0 {// 1) 先把当前子树的“最左链”全部入栈for cur != nil {stack = append(stack, cur) // pushcur = cur.Left}// 2) 栈顶弹出一个节点top := stack[len(stack)-1]      // 取顶stack = stack[:len(stack)-1]    // 弹出// 访问节点(中序的“根”)res = append(res, top.Val)// 3) 转向右子树,继续外层循环cur = top.Right}return res
}

递归实现

// 无注释纯享:
func inorderTraversal(root *TreeNode) []int {res := []int{}var dfs func(*TreeNode)dfs = func(node *TreeNode) {if node == nil {return}dfs(node.Left)res = append(res, node.Val)dfs(node.Right)}dfs(root)return res
}// 中序遍历(递归实现)
func inorderTraversal(root *TreeNode) []int {// 定义一个切片保存遍历结果res := []int{}// 定义递归函数 dfsvar dfs func(*TreeNode)dfs = func(node *TreeNode) {if node == nil { // 递归终止条件:遇到空节点return}// 1. 先递归遍历左子树dfs(node.Left)// 2. 访问当前节点,把节点值放入结果切片res = append(res, node.Val)// 3. 再递归遍历右子树dfs(node.Right)}// 从根节点开始递归dfs(root)// 返回最终的遍历结果return res
}

基础知识

中序遍历(Inorder Traversal)是二叉树遍历的一种方式,遍历顺序为:
遍历左子树->访问根节点->遍历右子树

定义了一个结构体 TreeNode,它代表二叉树的一个节点
Val 存储节点的值(int 类型)。

type TreeNode struct {Val   intLeft  *TreeNodeRight *TreeNode
}

Left 指向左子节点。
Right 指向右子节点。

TreeNode 表示 一个结构体本身。
*TreeNode 表示 指向 TreeNode 的指针。

函数与递归

var dfs func(*TreeNode)
dfs = func(node *TreeNode) { ... }

var dfs func(*TreeNode) 声明了一个变量 dfs,它的类型是函数,参数是 *TreeNode(指向二叉树节点的指针),无返回值。

dfs = func(node *TreeNode) { ... } 定义了这个函数的具体内容。

这种写法属于 匿名函数赋值给变量,常用于递归或闭包。

dfs(node.Left)  // 遍历左子树
dfs(node.Right) // 遍历右子树

104. 二叉树的最大深度

题目

给定一个二叉树 root ,返回其最大深度。

二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。

示例 1:
在这里插入图片描述
输入:root = [3,9,20,null,null,15,7]
输出:3

示例 2:

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

提示:

树中节点的数量在 [0, 104] 区间内。
-100 <= Node.val <= 100

总体思路

递归
使用深度优先搜索 DFS的递归方法来计算二叉树的最大深度。其核心思想是:

  1. 递归地计算左子树的最大深度
  2. 递归地计算右子树的最大深度
  3. 取左右子树深度的最大值,然后加上当前节点自身的深度1
  4. 基础情况:当节点为空时,返回深度0

时间复杂度与空间复杂度

时间复杂度:O(n)

  • 每个节点恰好被访问一次,n为二叉树中的节点数量

空间复杂度:O(h),其中h是树的高度

  • 空间复杂度主要由递归调用栈的深度决定
  • 在最坏情况下(树退化为链表),h = n,空间复杂度为O(n)
  • 在最好情况下(平衡二叉树),h = logn,空间复杂度为O(logn)
  • 平均情况下,空间复杂度为O(h)

深度优先搜索

迭代深度优先搜索(DFS) 来计算二叉树的最大深度。通过使用栈来模拟递归过程,显式地跟踪每个节点及其对应的深度,避免了递归调用的系统栈开销。

算法步骤

  1. 初始化:处理空树情况,初始化栈结构
  2. 根节点入栈:将根节点和初始深度1入栈
  3. 循环处理:不断从栈中弹出节点进行处理
  4. 深度更新:比较并更新最大深度
  5. 子节点入栈:将子节点和对应深度入栈
  6. 返回结果:栈空时返回最大深度

时间复杂度与空间复杂度

  • 时间复杂度:O(n)
    • 每个节点都被访问 exactly once
    • n 是树中的节点数量
    • 每个节点的入栈和出栈操作都是O(1)
  • 空间复杂度:O(n)
    • 最坏情况下栈的大小等于树的高度
    • 平衡二叉树:O(log n)
    • 链状二叉树(最坏情况):O(n)
    • 平均情况:O(log n)

广度优先搜索

使用**广度优先搜索(BFS)**的方法来计算二叉树的最大深度。其核心思想是:

  1. 从根节点开始,按层级遍历树的每个节点
  2. 使用队列来存储待处理的节点及其对应的深度
  3. 每次处理一个节点时,如果当前深度大于记录的最大深度,就更新最大深度
  4. 将当前节点的子节点(如果存在)加入队列,并标记为下一层深度
  5. 当队列为空时,说明所有节点都已处理完毕,返回记录的最大深度

时间复杂度与空间复杂度

时间复杂度:O(n)

  • 每个节点恰好被访问一次,n为二叉树中的节点数量

空间复杂度:O(n)

  • 在最坏情况下(完全二叉树),队列中最多会存储约n/2个节点
  • 在最好情况下(链状树),队列中最多会存储1个节点
  • 平均情况下,空间复杂度为O(n)

代码

golang

递归法

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {if root == nil {return 0}return max(maxDepth(root.Left),maxDepth(root.Right)) + 1
}
/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {// 基础情况:如果当前节点为空,返回深度0// 这是递归的终止条件if root == nil {return 0}// 递归计算左子树的最大深度leftDepth := maxDepth(root.Left)// 递归计算右子树的最大深度  rightDepth := maxDepth(root.Right)// 返回左右子树深度的最大值 + 1(当前节点自身的深度)// 这里使用了max函数(需要额外定义)return max(leftDepth, rightDepth) + 1
}// 辅助函数:返回两个整数中的较大值
func max(a, b int) int {if a > b {return a}return b
}

深度优先搜索

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {if root == nil {return 0}depth := 0stack := []struct{node *TreeNodelevel int}{}stack = append (stack, struct{node *TreeNode; level int}{root, 1})for len(stack) > 0 {temp := stack[len(stack)-1]stack = stack[:len(stack)-1]if temp.level > depth {depth = temp.level}if temp.node.Right != nil {stack = append(stack, struct{node *TreeNode; level int}{temp.node.Right, temp.level + 1})}if temp.node.Left != nil {stack = append(stack, struct{node *TreeNode; level int}{temp.node.Left, temp.level + 1})}}return depth
}
/*** 计算二叉树的最大深度(迭代版本)* 使用深度优先搜索(DFS)策略,通过栈实现迭代遍历* * @param root *TreeNode 二叉树的根节点* @return int 树的最大深度*/
func maxDepth(root *TreeNode) int {// 边界条件处理:空树深度为0if root == nil {return 0}// 初始化最大深度变量depth := 0// 定义栈元素的结构体// 每个栈元素包含一个树节点和该节点对应的深度type stackItem struct {node  *TreeNode  // 树节点level int        // 该节点所在的深度}// 初始化栈,使用切片模拟栈结构// Go语言中没有内置栈结构,使用切片实现栈功能stack := []stackItem{}// 将根节点和初始深度1压入栈中// 根节点深度为1,因为深度从1开始计数stack = append(stack, stackItem{root, 1})// 主循环:当栈不为空时继续处理// 循环条件:len(stack) > 0for len(stack) > 0 {// 弹出栈顶元素(后进先出LIFO)// 获取栈顶元素temp := stack[len(stack)-1]// 移除栈顶元素(切片截取)stack = stack[:len(stack)-1]// 更新最大深度// 比较当前节点的深度和已知最大深度,取较大值if temp.level > depth {depth = temp.level}// 处理右子节点(先压入右子节点,后处理)// 因为栈是LIFO结构,先压入的后弹出// 所以先压入右子节点,保证左子节点先被处理if temp.node.Right != nil {// 右子节点深度 = 当前节点深度 + 1stack = append(stack, stackItem{temp.node.Right, temp.level + 1})}// 处理左子节点(后压入左子节点,先处理)if temp.node.Left != nil {// 左子节点深度 = 当前节点深度 + 1stack = append(stack, stackItem{temp.node.Left, temp.level + 1})}}// 返回计算得到的最大深度return depth
}

广度优先搜索

/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {if root == nil {return 0}depth := 0queue := []struct{node *TreeNodelevel int}{}queue = append(queue, struct{node *TreeNode; level int}{root, 1})for len(queue) > 0 {temp := queue[0]queue = queue[1:]if temp.level > depth {depth = temp.level}if temp.node.Left != nil {queue = append(queue, struct{node *TreeNode; level int}{temp.node.Left, temp.level + 1})}if temp.node.Right != nil {queue = append(queue, struct{node *TreeNode; level int}{temp.node.Right, temp.level + 1})}}return depth
}
/*** Definition for a binary tree node.* type TreeNode struct {*     Val int*     Left *TreeNode*     Right *TreeNode* }*/
func maxDepth(root *TreeNode) int {// 如果根节点为空,直接返回深度0if root == nil {return 0}// 初始化最大深度depth := 0// 定义队列,使用结构体存储节点和对应的层级// 这里使用了匿名结构体:包含节点指针和层级信息queue := []struct{node  *TreeNode  // 树节点level int        // 该节点所在的深度层级}{}// 将根节点和初始深度1加入队列queue = append(queue, struct{node *TreeNode; level int}{root, 1})// 当队列不为空时循环处理for len(queue) > 0 {// 从队列头部取出一个元素(先进先出)temp := queue[0]queue = queue[1:]  // 移除队列头部元素// 如果当前节点的层级大于记录的最大深度,更新最大深度if temp.level > depth {depth = temp.level}// 如果当前节点有左子节点,将其加入队列// 子节点的深度为当前节点深度+1if temp.node.Left != nil {queue = append(queue, struct{node *TreeNode; level int}{temp.node.Left, temp.level + 1})}// 如果当前节点有右子节点,将其加入队列// 子节点的深度为当前节点深度+1if temp.node.Right != nil {queue = append(queue, struct{node *TreeNode; level int}{temp.node.Right, temp.level + 1})}}// 返回计算得到的最大深度return depth
}

文章转载自:

http://Y5Iq3ozk.cknws.cn
http://QMywd22a.cknws.cn
http://x9UcN9Vg.cknws.cn
http://BLn3ESif.cknws.cn
http://aDwHcCAQ.cknws.cn
http://7MOG3aTf.cknws.cn
http://mMArukC9.cknws.cn
http://BnQuVM29.cknws.cn
http://sP5rb7AX.cknws.cn
http://Nwe70ynZ.cknws.cn
http://qSDyG3LD.cknws.cn
http://0RazAs34.cknws.cn
http://2Q0FdmTT.cknws.cn
http://Yunm8gp8.cknws.cn
http://ZxNAfgtN.cknws.cn
http://WXBP31yV.cknws.cn
http://XNhbTIe2.cknws.cn
http://eGOKsxNi.cknws.cn
http://VWLLYNvN.cknws.cn
http://SM0TelIk.cknws.cn
http://lZf54XFQ.cknws.cn
http://z5Unidut.cknws.cn
http://Gl2wbHj5.cknws.cn
http://nZmwhZEI.cknws.cn
http://eQd70jlr.cknws.cn
http://j5vVstdq.cknws.cn
http://E99DFDaE.cknws.cn
http://XCuQ4JRv.cknws.cn
http://pF5fRVif.cknws.cn
http://GtRWfdg5.cknws.cn
http://www.dtcms.com/a/367340.html

相关文章:

  • 渗透测试与网络安全审计的关系
  • Qwen2.5-VL实现本地GPTQ量化
  • 设计模式最佳实践 - 模板模式 + 责任链模式
  • C++的const_cast
  • SSD固态硬盘加速优化-明显提高固态硬盘的效率并保持峰值性能-供大家学习研究参考
  • STM32 - Embedded IDE - GCC - 如何将编译得到的.bin固件添加CRC32校验码
  • VSCode中的扩展Extension说明
  • 《IC验证必看|semaphore与mailbox的核心区别》
  • Web与Nginx
  • JS 可迭代对象详解:从概念到实践的全方位剖析
  • 同城酒水推广算法怎么做?
  • (自用)PowerShell常用命令自查文档
  • 当公司在你电脑上安装了IP-guard,你必须知道的事
  • 【已更新文章+代码】2025数学建模国赛B题思路代码文章高教社杯全国大学生数学建模-碳化硅外延层厚度的确定
  • 空车不空,英魂长在(记9.3大阅兵)
  • MySQL并发问题解析
  • linux——自定义协议
  • 基于联邦学习的政务大数据平台应用研究
  • Jenkins调用ansible部署lnmp平台
  • 迈威通信从送快递角度教你分清网络二层和三层
  • 热计量表通过M-Bus接口实现无线集抄系统的几种解决方
  • 从KV Cache竞争到多卡优化:vLLM加载AWQ模型的显存优化全攻略
  • 8.7 通过时间反向传播
  • 基于扣子平台构造AutoGen框架的多智能体使用-----封装成FastAPI接口供调用
  • 谈谈你对ThreadLocal的理解
  • YOLOv11全方位改进指南:从Backbone到检测头的深度优化
  • PLC编程入门精通全套教程(附视频资料)
  • Spring启示录
  • Fiddler辅助标签+工具面板(柠檬班公开课2-2)
  • 云手机运行是否消耗自身流量?