Appearance
二叉树的最大深度:一遍递归即满分
本文属于「408 算法拓展训练」——考纲内、真题代码题尚未考过的经典题。二叉树最大深度是「树与二叉树」这一章最基础的一道后序遍历应用题,和 2014·41 二叉树 WPL 是同一族——一趟遍历带着状态往上传。它是经典训练题,不是真题,也不押题。
题目
给定一棵二叉树的根结点,求它的最大深度——从根结点到最远叶结点的最长路径上的结点数。空树的最大深度为 0,只有根结点的树最大深度为 1。
这道题没有「尽可能高效」的要求。按拓展训练导语的分类,它属于「直观即满分」型——直白写法就是最优解,力气花在把递归的边界和顺序写准上。
直观解法:一遍递归
一棵树的最大深度,等于「左子树的最大深度」和「右子树的最大深度」中较大的那个,再加上根结点自己这一层。这是一个天然的递归定义:子问题(左右子树的深度)算完之后,用它们组合出当前结点的答案——这正是后序遍历的顺序(先处理左右子树,再处理根)。
c
typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
int maxDepth(TreeNode *root) {
if (root == NULL) return 0; // 空树深度为 0
int leftDepth = maxDepth(root->left);
int rightDepth = maxDepth(root->right);
return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1; // 较大者加一
}拿一棵简单的树走一遍:根 1,左孩子 2,2 又有左孩子 4;根 1 还有右孩子 3。递归到 4 时左右孩子都是 NULL,maxDepth(4) 返回 1;结点 2 的左深度是 1(来自 4)、右深度是 0(没有右孩子),取较大值 1 再加一,maxDepth(2) 返回 2;结点 3 没有孩子,maxDepth(3) 返回 1;根结点左深度 2(来自结点 2)、右深度 1(来自结点 3),取较大值 2 再加一,最终返回 3。
复杂度:每个结点恰好被访问一次、做常数次比较,时间
为什么它就是满分解
用总纲的「找浪费三问」审一遍,三问全部答「没有」:
- 有没有重复扫描? 没有。每个结点只在自己所在的那次递归调用里被访问一次,不会因为求别的结点的深度而被重复访问。
- 有没有重算已知的东西? 没有。左右子树的深度各自只算一次,算完立刻用来组合出当前结点的答案,不存在同一棵子树的深度被外层多次重新计算的情况。
- 有没有放着性质不用? 没有。题目要的「子树深度」正好靠递归的返回值自然向上传递,不需要额外的数据结构去记录。
更根本的一条:要确定最大深度,至少要知道树上每个结点是否存在(否则漏看的那个结点如果恰好在最深的那条路径上,答案就错了)——访问一遍所有结点就是
真正的难点在边界和顺序
分不在优化上,在三处容易翻车的地方:
- 空树的深度是 0,不是 -1、也不是异常。递归的终止条件
root == NULL返回 0,是整个递归能正确工作的地基——如果这个基准返回值错了,往上传递的每一层结果都会跟着错位。 - 「加一」必须在两个子问题的结果都拿到之后。先递归求出
leftDepth和rightDepth,再用它们的较大值加一得到当前结点的深度——这个顺序正是后序遍历「先子树、后根」的体现。如果在递归调用之前就想着加一,会让代码逻辑和递归定义脱节,容易写错。 - 只有一个叶结点时,深度算的是 1,不是 0。叶结点的左右孩子都是 NULL,两次递归调用都返回 0,取较大值 0 再加一得到 1——手算一棵只有根结点的树,是验证边界写对没有的最快方法。
一遍递归,看一遍
用一棵简单的树(根 1,左孩子 2 挂着左孩子 4,右孩子 3)演示后序遍历式的深度计算——先递归到最深的叶结点算出 0,逐层往上取较大值加一:
演示为 JavaScript 版本,逻辑与上文 C 代码一一对应。
别画蛇添足
这道题结构简单,过度设计的空间不大,但有两个常见的「想复杂了」:
- 想额外维护一个全局最大值变量,自顶向下传递当前深度。这种写法(
dfs(node, depth),每到一个结点就用depth更新全局最大值)确实也能算对,复杂度也是,但它引入了额外的全局状态和参数传递,而后序遍历「返回值自底向上组合」的写法不需要任何额外状态,更短也更不容易出错——两种写法效率相当,选更简洁的那种。 - 想用层序遍历(BFS)数层数。层序遍历确实也能求出最大深度(数一共扫了几层),但需要额外维护一个队列,代码量比递归大不少,对这道题而言没有必要。
下一步
- 看二叉树后序遍历的交互演示:后序遍历可视化
- 同属「树与二叉树」的拓展题:判断平衡二叉树、判断对称二叉树、判断完全二叉树
- 回到方法论源头:「暴力解到最优解」真题专题总纲