题目描述
思路解析
一句话答案:LeetCode 104 二叉树的最大深度用后序递归几行解决:空节点深度为 0,非空节点深度 = max(左子树深度, 右子树深度) + 1。必须先算完左右孩子才能算自己,所以是后序遍历的形状。每个节点访问一次,时间 O(n),递归栈空间 O(h)。
最大深度数的是节点还是边
题目定义的深度是「从根到最远叶子这条路径上的节点个数」:空树深度 0,只有一个根的树深度 1。这个口径要先钉死——如果按边数理解,单根树会算成 0,整个答案系统性地差 1。输入是一棵普通二叉树,输出一个整数。
为什么求树的深度自然想到递归
直接想「根到最远叶子有多远」不好下手,因为最远的叶子可能藏在任何一支里。换个问法就豁然开朗:整棵树的深度,跟它两棵子树的深度是什么关系?答案是——树的最深路径必然从根出发、走进左右子树之一,所以整棵树的深度 = 两棵子树深度的较大者再加上根自己这一层,即 max(left, right) + 1。
这一步把「算一棵大树」拆成了「算两棵小树」这两个一模一样的子问题,而子问题还能继续往下拆,直到空节点——空树没有任何节点,深度定为 0,这就是递归出口。整个解法就是把这条关系式原样写成代码。
为什么是后序遍历而不是前序
关系式 max(left, right) + 1 里,父节点的答案依赖两个孩子的答案:不先知道左右子树各有多深,就不知道该给谁加一。所以计算顺序必须是「先左子树、再右子树、最后自己」,这正是后序遍历的定义。落到执行上,递归会一路扎到最底层,叶子的深度最先确定(左右都是空,max(0,0)+1 = 1),然后一层层往上回传,每上一层取较大值加一,回到根时得到的就是整棵树的最大深度。
这个递归为什么一定算得对
正确性可以用归纳法一句话说清:空树深度 0 显然正确;假设左右子树的递归结果都对,那么根到最远叶子的路径必定经过较深的那棵子树,长度就是那棵子树的深度加上根这一个节点——恰好是 max(left, right) + 1。底层对、每一步组合也对,整体就对。这也是大多数二叉树递归题共同的论证套路:只要「出口正确 + 组合关系正确」,就不必担心中间过程。
复杂度与最容易写错的边界
时间 O(n):每个节点恰好被访问一次,每次只做一次比较和一次加法。空间 O(h):递归栈最深等于树高 h,平衡树约 O(log n),退化成链时 O(n)。
两个高频错误:一是递归出口把空节点深度写成 1——出口是整个递归的地基,写 1 会让所有答案多 1;二是把深度当成边数返回 max(left, right) 而忘了 +1,那个 +1 代表的是当前节点自己,漏掉答案就永远是 0。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
这正是后序遍历的形状:必须先把左右子树都算完,才能算自己。所以递归会一路扎到最底层的叶子,叶子的深度先定下来(都是 1),再一层层往上回传、取较大值 +1。下面逐节点看这个过程。
准备 · 整棵树:先看清这棵树:根是 3,往下到最远的叶子要走几层?接下来从根出发做后序 DFS——一路扎到底,叶子的深度先定,再一层层往上回传。
访问节点 3:深度优先往下走,现在踩到节点 3。后序遍历的规矩是「左右子树先算完,自己才算」,所以先把它挂在递归栈上(绿),继续往左右孩子深入。
访问节点 9:深度优先往下走,现在踩到节点 9。后序遍历的规矩是「左右子树先算完,自己才算」,所以先把它挂在递归栈上(绿),继续往左右孩子深入。
访问节点 4:深度优先往下走,现在踩到节点 4。后序遍历的规矩是「左右子树先算完,自己才算」,所以先把它挂在递归栈上(绿),继续往左右孩子深入。
访问节点 2 · 叶子:走到叶子节点 2。它没有孩子,左右子树深度都是 0,所以它自己的深度 = max(0,0)+1 = 1。后序遍历的妙处:最底层的答案最先确定。
回传 2 · 深度 = 1:节点 2 的左右子树都回来了:左空 0、右空 0。取较深的那个 +1,得到它的深度 = max(0,0)+1 = 1。算完就把它标蓝(已定),深度回传给父节点。
访问节点 1 · 叶子:走到叶子节点 1。它没有孩子,左右子树深度都是 0,所以它自己的深度 = max(0,0)+1 = 1。后序遍历的妙处:最底层的答案最先确定。
回传 1 · 深度 = 1:节点 1 的左右子树都回来了:左空 0、右空 0。取较深的那个 +1,得到它的深度 = max(0,0)+1 = 1。算完就把它标蓝(已定),深度回传给父节点。
回传 4 · 深度 = 2:节点 4 的左右子树都回来了:左子树 1、右子树 1。取较深的那个 +1,得到它的深度 = max(1,1)+1 = 2。算完就把它标蓝(已定),深度回传给父节点。
访问节点 7 · 叶子:走到叶子节点 7。它没有孩子,左右子树深度都是 0,所以它自己的深度 = max(0,0)+1 = 1。后序遍历的妙处:最底层的答案最先确定。
回传 7 · 深度 = 1:节点 7 的左右子树都回来了:左空 0、右空 0。取较深的那个 +1,得到它的深度 = max(0,0)+1 = 1。算完就把它标蓝(已定),深度回传给父节点。
回传 9 · 深度 = 3:节点 9 的左右子树都回来了:左子树 2、右子树 1。取较深的那个 +1,得到它的深度 = max(2,1)+1 = 3。算完就把它标蓝(已定),深度回传给父节点。
访问节点 20:深度优先往下走,现在踩到节点 20。后序遍历的规矩是「左右子树先算完,自己才算」,所以先把它挂在递归栈上(绿),继续往左右孩子深入。
访问节点 15 · 叶子:走到叶子节点 15。它没有孩子,左右子树深度都是 0,所以它自己的深度 = max(0,0)+1 = 1。后序遍历的妙处:最底层的答案最先确定。
回传 15 · 深度 = 1:节点 15 的左右子树都回来了:左空 0、右空 0。取较深的那个 +1,得到它的深度 = max(0,0)+1 = 1。算完就把它标蓝(已定),深度回传给父节点。
访问节点 8 · 叶子:走到叶子节点 8。它没有孩子,左右子树深度都是 0,所以它自己的深度 = max(0,0)+1 = 1。后序遍历的妙处:最底层的答案最先确定。
回传 8 · 深度 = 1:节点 8 的左右子树都回来了:左空 0、右空 0。取较深的那个 +1,得到它的深度 = max(0,0)+1 = 1。算完就把它标蓝(已定),深度回传给父节点。
回传 20 · 深度 = 2:节点 20 的左右子树都回来了:左子树 1、右子树 1。取较深的那个 +1,得到它的深度 = max(1,1)+1 = 2。算完就把它标蓝(已定),深度回传给父节点。
回传 3 · 深度 = 4:节点 3 的左右子树都回来了:左子树 3、右子树 2。取较深的那个 +1,得到它的深度 = max(3,2)+1 = 4。算完就把它标蓝(已定),深度回传给父节点。
答案 · 最大深度 = 4:回到根节点 3:它左子树深度 3、右子树深度 2,取较深的 +1 = 4。这就是整棵树的最大深度——最长的一条路是 3→9→4→2 这样的 4 层。
参考代码
class Solution { public int maxDepth(TreeNode root) { if (root == null) return 0; // 空节点深度 0(出口) int left = maxDepth(root.left); // 后序:先算左 int right = maxDepth(root.right); // 再算右 return Math.max(left, right) + 1; // 较深子树 + 1 }}复杂度
- 时间:O(n),每个节点恰好访问一次
- 空间:O(h),递归栈深 = 树高 h;最坏(链)O(n),平衡 O(log n)
易错点
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
二叉树的直径
LeetCode 543 · 简单 · 沿着 树 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题