力扣解题-104. 二叉树的最大深度
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:3
示例 2:
输入:root = [1,null,2]
输出:2
提示:
树中节点的数量在 [0, 10⁴] 区间内。
-100 <= Node.val <= 100
Related Topics
树、深度优先搜索、广度优先搜索、二叉树
第一次解答
解题思路
核心方法:递归法(深度优先搜索 DFS),利用二叉树的递归结构,将“求整棵树的最大深度”拆解为“求左右子树的最大深度 + 当前节点的深度1”,基线条件为“空节点深度为0”,逻辑简洁且时间/空间复杂度最优。
核心逻辑拆解
二叉树最大深度的递归求解符合“分治思想”,核心是“自底向上”计算深度:
- 基线条件(递归终止):若当前节点
root == null(空节点),返回深度0(空树没有节点,深度为0); - 递归分解问题:
- 递归计算左子树的最大深度:
leftDepth = maxDepth(root.left); - 递归计算右子树的最大深度:
rightDepth = maxDepth(root.right);
- 递归计算左子树的最大深度:
- 合并结果:当前节点所在的树的最大深度 = 1(当前节点本身) + 左右子树深度的最大值(
Math.max(leftDepth, rightDepth)); - 返回结果:最终返回根节点对应的最大深度,即为整棵树的最大深度。
具体步骤(以示例1 root=[3,9,20,null,null,15,7]为例)
| 递归层级 | 节点 | leftDepth | rightDepth | 计算结果 | 说明 |
|---|---|---|---|---|---|
| 1 | 3 | 1 | 2 | 3 | 根节点深度=1+max(1,2)=3 |
| 2 | 9 | 0 | 0 | 1 | 9的左右子树为空,深度=1 |
| 2 | 20 | 1 | 1 | 2 | 20的深度=1+max(1,1)=2 |
| 3 | 15 | 0 | 0 | 1 | 15的左右子树为空,深度=1 |
| 3 | 7 | 0 | 0 | 1 | 7的左右子树为空,深度=1 |
| 最终结果为3,与示例一致。 |
性能说明
- 时间复杂度:O(n)(每个节点仅被访问一次,n为节点总数);
- 空间复杂度:O(h)(h为树的高度,递归调用栈的深度等于树的高度):
- 最好情况(平衡二叉树):h = log₂n,空间复杂度O(logn);
- 最坏情况(斜树,如链表):h = n,空间复杂度O(n);
- 优势:
- 代码极简,仅几行逻辑完成核心计算,符合二叉树的递归特性;
- 无额外数据结构开销,执行效率最高;
- 天然处理空树、单节点树等边界场景。
public int maxDepth(TreeNode root) {
// 基线条件:空节点深度为 0
if (root == null) {
return 0;
}
// 递归计算左右子树的最大深度
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
// 当前节点的深度 = 1 + 左右子树深度的最大值
return 1 + Math.max(leftDepth, rightDepth);
}
示例解答
解题思路
解法1:迭代法(广度优先搜索 BFS / 层序遍历)
核心方法:层序遍历统计层数,利用队列实现二叉树的层序遍历,每遍历完一层,层数加1,最终层数即为二叉树的最大深度,属于“自顶向下”的求解方式,空间复杂度与递归法一致,但避免了递归栈的调用。
代码实现
import java.util.LinkedList;
import java.util.Queue;
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 队列存储当前层的节点
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
// 层序遍历
while (!queue.isEmpty()) {
// 当前层的节点数
int levelSize = queue.size();
// 遍历当前层所有节点
for (int i = 0; i < levelSize; i++) {
TreeNode curr = queue.poll();
// 左子节点入队
if (curr.left != null) {
queue.offer(curr.left);
}
// 右子节点入队
if (curr.right != null) {
queue.offer(curr.right);
}
}
// 每遍历完一层,深度加1
depth++;
}
return depth;
}
核心逻辑说明
- 边界处理:空树直接返回0;
- 队列初始化:根节点入队,深度初始化为0;
- 层序遍历:
- 每次循环处理一层节点(通过
levelSize记录当前层节点数); - 遍历当前层所有节点,将子节点入队;
- 每处理完一层,深度
depth加1;
- 每次循环处理一层节点(通过
- 返回结果:最终
depth即为最大深度。
性能说明
- 时间复杂度:O(n)(每个节点仅入队/出队一次);
- 空间复杂度:O(n)(最坏情况队列存储一层所有节点,如完全二叉树的最后一层有n/2个节点);
- 优势:非递归实现,避免递归栈溢出风险(如树高度极大时);
- 劣势:需要额外的队列空间,代码量略多于递归法。
解法2:迭代法(深度优先搜索 DFS / 栈模拟递归)
核心方法:栈模拟递归过程,用栈存储“节点+当前深度”的组合,遍历过程中更新最大深度,属于“自顶向下”的DFS实现,逻辑与递归法等价但无递归栈开销。
代码实现
import java.util.Stack;
public int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 栈存储<节点, 深度>,用数组模拟
Stack<Object[]> stack = new Stack<>();
stack.push(new Object[]{root, 1});
int maxDepth = 0;
while (!stack.isEmpty()) {
Object[] arr = stack.pop();
TreeNode curr = (TreeNode) arr[0];
int depth = (int) arr[1];
// 更新最大深度
maxDepth = Math.max(maxDepth, depth);
// 先压右子节点(栈后进先出,保证左子节点先处理)
if (curr.right != null) {
stack.push(new Object[]{curr.right, depth + 1});
}
// 再压左子节点
if (curr.left != null) {
stack.push(new Object[]{curr.left, depth + 1});
}
}
return maxDepth;
}
核心逻辑说明
- 栈初始化:根节点入栈,初始深度为1;
- 栈遍历:
- 弹出栈顶元素,获取当前节点和深度;
- 更新最大深度;
- 按“先右后左”的顺序压入子节点(栈是后进先出,保证左子节点先被处理,与递归顺序一致);
- 返回结果:最终
maxDepth即为最大深度。
性能说明
- 时间复杂度:O(n)(每个节点仅入栈/出栈一次);
- 空间复杂度:O(h)(h为树的高度,栈的深度等于树的高度);
- 优势:非递归实现,可控性更高,避免递归栈溢出;
- 劣势:代码量多于递归法,需要手动管理栈和深度。
总结
- 递归DFS法(第一次解答):O(n)时间+O(h)空间,代码极简、逻辑直观,是二叉树最大深度的最优解法,工程中优先使用;
- 迭代BFS法(层序遍历):O(n)时间+O(n)空间,非递归实现,适合需要避免递归栈的场景;
- 迭代DFS法(栈模拟):O(n)时间+O(h)空间,与递归法空间复杂度一致,代码稍复杂;
- 关键技巧:
- 核心思想:二叉树的最大深度 = 1 + max(左子树深度, 右子树深度),空节点深度为0;
- 方法选择:优先选递归DFS法(代码简洁),树高度极大时选迭代法(避免栈溢出);
- 边界处理:空树直接返回0,单节点树返回1,天然覆盖所有边界场景。