#LC104. 二叉树的最大深度

二叉树的最大深度

你好!我是你的 OI 金牌教练。今天我们来探讨一个树论中的入门级必考题——二叉树的最大深度

在 NOI 系列竞赛中,树的深度不仅是一个基本属性,它是许多高级算法(如倍增 LCA、树链剖分、平衡树)中维护复杂度的核心指标。


一、 题目描述 (NOI 规范版)

题目名称:二叉树的最大深度 (max_depth_binary_tree) 时间限制:1.0s 内存限制:256MB

【问题描述】 给定一棵二叉树的根节点 rootroot,请你计算该二叉树的最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 (注:叶子节点是指没有子节点的节点。)

【输入格式】 输入文件包含多行,描述二叉树的结构: 第一行一个整数 nn,表示节点总数。 接下来的 nn 行,每行描述一个节点的信息:首先是该节点的值 vv,然后是该节点的左子节点编号 LL,最后是右子节点编号 RR。 (注:节点编号从 0 到 n1n-1,根节点固定为 0。如果子节点不存在,则编号为 -1。如果 n=0n=0,则表示空树。)

【输出格式】 输出一个整数,表示二叉树的最大深度。

【样例 1 输入】

5
3 1 2
9 -1 -1
20 3 4
15 -1 -1
7 -1 -1

【样例 1 输出】

3

【样例 2 输入】

2
1 -1 1
2 -1 -1

【样例 2 输出】

2

【数据范围】

  • 节点总数 0n1040 \le n \le 10^4
  • 节点值在 [100,100][-100, 100] 之间

二、 预备知识点

  1. 二叉树结构:理解左子树、右子树以及叶子节点的概念。
  2. DFS (深度优先搜索):利用递归从上往下探索路径。
  3. 分治思想:整棵树的高度 = 1 + max(左子树高度, 右子树高度)。
  4. BFS (广度优先搜索):利用队列按层遍历,层数即为深度。

三、 启发式思维导图:草稿纸上的推理

拿出一张草稿纸,我们一起来推导这个过程:

1. 递归思维 (自底向上)

画出一个简单的三层二叉树:

  • 看到根节点时,我想:如果我知道左边多高,右边多高,那我就选高的那个加 1 就可以了。
  • 于是我问左儿子:“你多高?”
  • 左儿子如果没孩子,他说:“我这只有 1 层”。
  • 递归的终点:如果这个位置根本没有节点(空),那高度就是 0。

2. 迭代思维 (自顶向下)

  • 想象往树根注水,水按层流下。
  • 第一层流完,深度计为 1。
  • 把第一层产生的所有儿子放进“待处理”名单,这就是第二层。
  • 只要名单不为空,深度就加 1。

3. 复杂度预估

  • 时间复杂度:每个节点必须看一遍,所以是 O(N)O(N)
  • 空间复杂度
    • 递归取决于树的高度。最坏情况(树变成了一条链)是 O(N)O(N)
    • 迭代取决于树的最宽处(队列的大小),最坏情况也是 O(N)O(N)

四、 算法思路提示 (伪代码流程图)

在 NOI 竞赛中,递归实现最为简洁,迭代实现最为稳健。

方案 A:递归 DFS (分治法)

graph TD
    Start(开始 DFS node) --> CheckNull{node 编号是否等于 -1}
    CheckNull -- 是 --> ReturnZero(返回 0)
    CheckNull -- 否 --> RecurseLeft(递归计算左子树深度 LeftH)
    RecurseLeft --> RecurseRight(递归计算右子树深度 RightH)
    RecurseRight --> Calc(取 LeftH 与 RightH 的最大值并加 1)
    Calc --> ReturnVal(返回计算结果)

方案 B:迭代法 (BFS 层序遍历)

graph TD
    Start(开始 BFS) --> CheckEmpty{n 是否为 0}
    CheckEmpty -- 是 --> Ret0(返回深度 0)
    CheckEmpty -- 否 --> Init(初始化队列 Q 并存入根节点 0)
    Init --> SetD(设置 depth 等于 0)
    WhileQ{Q 是否为空} -- 否 --> LevelProc(深度 depth 自增 1)
    LevelProc --> GetSize(获取当前层节点数量 sz)
    GetSize --> ForLoop(循环处理当前层的 sz 个节点)
    ForLoop --> Pop(取出队头节点 u)
    Pop --> PushChildren(将 u 的左右子节点入队 Q)
    PushChildren --> ForLoop
    ForLoop -- 循环结束 --> WhileQ
    WhileQ -- 是 --> Final(返回 depth)

五、 关键读题技巧:哪里是“解题钥匙”?

  1. 关键词:最大 (Maximum)
    • 这暗示了我们需要在多条路径中寻找极值,通常涉及 max() 函数。
  2. 关键词:深度 (Depth)
    • 在树中,“深度”和“高度”在求解最大值时逻辑是一致的。记住核心公式:H = max(H_left, H_right) + 1
  3. 注意 n=0n=0 的特判
    • LeetCode 和 NOI 都非常看重边界条件。空树的深度是 0,这是最容易漏掉的测试点。
  4. 输入格式的差异
    • LeetCode 习惯直接给指针,而 NOI 习惯给编号或数组。
    • 在做题时,先建好结构体数组 struct Node { int l, r; } tree[MAXN]; 往往比动态申请内存更高效、更安全。

教练寄语: 这道题是通往“树形 DP”的大门。当你理解了如何通过子树的信息汇总出当前节点的信息,你就已经掌握了树形动态规划的核心思想。加油!