力扣解题-104. 二叉树的最大深度

0 阅读6分钟

力扣解题-104. 二叉树的最大深度

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

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

示例 1:

image.png

输入: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”,逻辑简洁且时间/空间复杂度最优。

核心逻辑拆解

二叉树最大深度的递归求解符合“分治思想”,核心是“自底向上”计算深度:

  1. 基线条件(递归终止):若当前节点root == null(空节点),返回深度0(空树没有节点,深度为0);
  2. 递归分解问题
    • 递归计算左子树的最大深度:leftDepth = maxDepth(root.left)
    • 递归计算右子树的最大深度:rightDepth = maxDepth(root.right)
  3. 合并结果:当前节点所在的树的最大深度 = 1(当前节点本身) + 左右子树深度的最大值(Math.max(leftDepth, rightDepth));
  4. 返回结果:最终返回根节点对应的最大深度,即为整棵树的最大深度。
具体步骤(以示例1 root=[3,9,20,null,null,15,7]为例)
递归层级节点leftDepthrightDepth计算结果说明
13123根节点深度=1+max(1,2)=3
290019的左右子树为空,深度=1
22011220的深度=1+max(1,1)=2
31500115的左右子树为空,深度=1
370017的左右子树为空,深度=1
最终结果为3,与示例一致。
性能说明
  • 时间复杂度:O(n)(每个节点仅被访问一次,n为节点总数);
  • 空间复杂度:O(h)(h为树的高度,递归调用栈的深度等于树的高度):
    • 最好情况(平衡二叉树):h = log₂n,空间复杂度O(logn);
    • 最坏情况(斜树,如链表):h = n,空间复杂度O(n);
  • 优势:
    1. 代码极简,仅几行逻辑完成核心计算,符合二叉树的递归特性;
    2. 无额外数据结构开销,执行效率最高;
    3. 天然处理空树、单节点树等边界场景。
    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;
}
核心逻辑说明
  1. 边界处理:空树直接返回0;
  2. 队列初始化:根节点入队,深度初始化为0;
  3. 层序遍历
    • 每次循环处理一层节点(通过levelSize记录当前层节点数);
    • 遍历当前层所有节点,将子节点入队;
    • 每处理完一层,深度depth加1;
  4. 返回结果:最终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. 栈初始化:根节点入栈,初始深度为1;
  2. 栈遍历
    • 弹出栈顶元素,获取当前节点和深度;
    • 更新最大深度;
    • 按“先右后左”的顺序压入子节点(栈是后进先出,保证左子节点先被处理,与递归顺序一致);
  3. 返回结果:最终maxDepth即为最大深度。
性能说明
  • 时间复杂度:O(n)(每个节点仅入栈/出栈一次);
  • 空间复杂度:O(h)(h为树的高度,栈的深度等于树的高度);
  • 优势:非递归实现,可控性更高,避免递归栈溢出;
  • 劣势:代码量多于递归法,需要手动管理栈和深度。

总结

  1. 递归DFS法(第一次解答):O(n)时间+O(h)空间,代码极简、逻辑直观,是二叉树最大深度的最优解法,工程中优先使用;
  2. 迭代BFS法(层序遍历):O(n)时间+O(n)空间,非递归实现,适合需要避免递归栈的场景;
  3. 迭代DFS法(栈模拟):O(n)时间+O(h)空间,与递归法空间复杂度一致,代码稍复杂;
  4. 关键技巧:
    • 核心思想:二叉树的最大深度 = 1 + max(左子树深度, 右子树深度),空节点深度为0;
    • 方法选择:优先选递归DFS法(代码简洁),树高度极大时选迭代法(避免栈溢出);
    • 边界处理:空树直接返回0,单节点树返回1,天然覆盖所有边界场景。