二叉树的最大深度 图解题解
从根到最远叶子有多深?每个节点只需问两个孩子,然后取大加一。
二叉树最大深度就像问一个家族树最多传了几代:每个人只需问左右两个孩子各自下面还有多深,取较深的一支 +1(算上自己)往上报。一层层把答案汇总到根,根收到的就是全树深度。空节点报 0,是递归触底的起点,不会多算一层。
这道题到底在问什么
- 树
- 3 →(9, 20),20 →(15, 7)
- 输出
- 3
先想最直接的笨办法
直觉是枚举所有「根→叶」路径数节点取最长。但同一段子树会被多条路径重复走,做了重复功——3→20 这段在 3→20→15 和 3→20→7 里就各走了一次,越靠上的子树被重走得越多。(动画第 3 步)
最优解:一步一步想明白
- 3直觉是枚举所有「根→叶」路径数节点取最长。但同一段子树会被多条路径重复走,做了重复功——3→20 这段在 3→20→15 和 3→20→7 里就各走了一次,越靠上的子树被重走得越多。
- 4转折:每个节点的深度只取决于它左右子树各自的深度,取较大的 + 1(算上自己这层)。所以一次后序遍历、每棵子树只算一次就够,不用重复走路径。空树深度 = 0,是递归触底的基准。
- 5准备递归从根 3 进入递归。后序的规矩:先把左右子树的深度都问出来,再算自己。先往左孩子 9 走。
- 6depth(9)=19 没有孩子,左右子树深度都是 0,depth(9) = 1 + max(0,0) = 1。把 1 返回给父亲 3。
- 7下探 20 的子树回到根,走右孩子 20。它有孩子,不能直接算,得先下探到 15、7。(9 已算完,标灰)
- 8depth(15)=115 是叶子,depth(15) = 1。返回给父亲 20。
- 9depth(7)=17 也是叶子,depth(7) = 1。返回给 20。现在 20 的左右深度都拿到了。
- 10depth(20)=220 结算:depth(20) = 1 + max(depth(15)=1, depth(7)=1) = 2。把 2 返回给根 3。
- 11L=1, R=2根 3 的两个孩子都回来了:左深 L=1、右深 R=2。这里是关键:要取较大的那条,不能只看左边的 1。
- 12depth = 3 ✓depth(3) = 1 + max(1, 2) = 3,对应最长路径 3→20→15(或 7)。若只走左边 9 那条会得 2,漏掉更深的右子树——这正是要 max 的原因。
- 15一句话记住这道题:父节点的深度 = 1 + max(左深, 右深)。错点全在这个 max——相加会把两条岔路当成一条路,只看一边会漏掉更深的子树。空树是 0、叶子是 1,这就是全部。
- 21看 merge 怎么换:LC111 最小深度取 1+min(L,R),但单孩子时空的那边不能算(否则被 0 拖成 1,要走有孩子的那侧);LC110 判平衡合并时多查一句 |L−R|≤1,不平衡就回传 −1 剪枝;LC543 直径答案不是返回值,而是过程中维护 max(L+R);LC124 最大路径和负分支要和 0 取 max 后再上传。每道都改这一处。更多在 二叉树专题。
⚠️ 容易写错的地方
✗ 错:空树返回 1
✓ 对:空树返回 0
空树没有节点,深度是 0,叶子才是 1
✗ 错:只算一条路径 / 把左右相加
✓ 对:取左右子树的 max 再 +1
深度看最长那条路,岔路不能拼成一条
完整代码(Python / C++ / Java)
Python
class Solution:
def maxDepth(self, root):
if not root: return 0 # 空树,触底
L = self.maxDepth(root.left) # 左子树深
R = self.maxDepth(root.right) # 右子树深
return 1 + max(L, R) # 取较大 +1C++
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0; // 空树,触底
int L = maxDepth(root->left); // 左子树深
int R = maxDepth(root->right); // 右子树深
return 1 + max(L, R); // 取较大 +1
}
};Java
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0; // 空树,触底
int L = maxDepth(root.left); // 左子树深
int R = maxDepth(root.right); // 右子树深
return 1 + Math.max(L, R); // 取较大 +1
}
}套路模板 · 自底向上树形递归(背下来)
def dfs(node):
if not node: return 基准值 # 空节点的答案
L = dfs(node.left) # 左子树的答案
R = dfs(node.right) # 右子树的答案
return 合并(L, R, node) # 拼成当前节点的答案int dfs(TreeNode* node) {
if (!node) return base; // 空节点的答案
int L = dfs(node->left); // 左子树答案
int R = dfs(node->right); // 右子树答案
return merge(L, R, node); // 拼成当前答案
}int dfs(TreeNode node) {
if (node == null) return base; // 空节点的答案
int L = dfs(node.left); // 左子树答案
int R = dfs(node.right); // 右子树答案
return merge(L, R, node); // 拼成当前答案
}复杂度
时间复杂度
O(n)
每个节点算一次
空间复杂度
O(h)
递归栈深 = 树高 h;最坏退化成链 h=n → O(n),平衡树 h=log n → O(log n)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 二叉树的最大深度 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这个递归函数返回的到底是什么?+
返回「以当前节点为根的子树」的最大深度——不是全局答案,是局部子树的高度,逐层往上拼。
为什么是后序(先左右、再自己),不是先序?+
因为算自己要先知道左右子树的深度,必须等两个孩子都返回了才能 1+max,这种「依赖孩子结果」的顺序天然就是后序。
深度(depth)和高度(height)是一回事吗?+
本题里从根往下数到最远叶子,数值上等于根的高度。严格说深度是「从根往下」、高度是「从叶往上」,这道题二者在根处相等,所以常被混用。
不用递归能做吗?+
能。BFS 一层层往下数,每弹完一整层深度就 +1,弹空时的层数就是最大深度,用一个队列实现。
空间为什么是 O(h) 而不是 O(n)?+
递归栈同一时刻只压着「当前这一条根到叶的路径」,最深就是树高 h;只有链状树退化时 h=n 才到 O(n)。
树很深会爆栈吗?+
会。极端链状树递归层数 = 节点数,可能栈溢出;工程上可改成显式栈或上面的 BFS 来避免。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。