一、题目描述
给定一个二叉树 root,返回其 最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
二、解题思路总览
| 方法 | 核心思想 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| 递归(DFS) | 后序遍历,左右子树深度+1 | O(n) | O(h),h为树高 |
| 迭代(BFS) | 层序遍历,统计层数 | O(n) | O(n),队列最大层宽度 |
选择建议:
- 递归:代码简洁,面试首选
- 迭代:直观展示层数,不需要递归思维
三、完整代码
方法一:递归(DFS 后序遍历)
cpp
class Solution {
public:
int maxDepth(TreeNode* root) {
// 递归终止条件:空节点深度为0
if (!root) return 0;
// 递归计算左子树深度
int leftDepth = maxDepth(root->left);
// 递归计算右子树深度
int rightDepth = maxDepth(root->right);
// 返回较大深度+1(加上当前根节点)
return max(leftDepth, rightDepth) + 1;
}
};
方法二:迭代(BFS 层序遍历)
cpp
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0; // 空树深度为0
queue<TreeNode*> q; // 层序遍历队列
q.push(root); // 根节点入队
int depth = 0; // 深度计数器
while (!q.empty()) { // 队列不为空时循环
int size = q.size(); // 当前层节点数
// 遍历当前层所有节点
for (int i = 0; i < size; i++) {
TreeNode* node = q.front();
q.pop();
// 把下一层子节点入队
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
depth++; // 处理完一层,深度+1
}
return depth;
}
};
四、算法流程图(ASCII)
递归版流程
以 root = [3, 9, 20, null, null, 15, 7] 为例:
3 深度 = max(左深度, 右深度) + 1
/ \
9 20 max(1, 2) + 1 = 3
/ \
15 7
递归展开过程:
maxDepth(3)
│
├── maxDepth(9) ──→ return 1 (叶子节点)
│
└── maxDepth(20)
│
├── maxDepth(15) ──→ return 1
└── maxDepth(7) ──→ return 1
计算:max(1, max(1,1)+1)+1 = max(1, 2)+1 = 3
迭代版流程
层序遍历,统计层数
第1层:队列[3] depth=1
第2层:队列[9,20] depth=2
第3层:队列[15,7] depth=3
第4层:队列[] depth=3,结束
最大深度 = 3
五、逐行解析
递归版逐行解析
cpp
class Solution {
public:
int maxDepth(TreeNode* root) {
// ─────────────────────────────────────────
// 第1步:递归终止条件
// 空节点没有子树,深度为0
// 这是递归的出口
// ─────────────────────────────────────────
if (!root) return 0;
// ─────────────────────────────────────────
// 第2步:递归计算左子树深度
// 后序遍历:先处理左子树
// 递归调用会一直深入到叶子节点
// ─────────────────────────────────────────
int leftDepth = maxDepth(root->left);
// ─────────────────────────────────────────
// 第3步:递归计算右子树深度
// 后序遍历:再处理右子树
// ─────────────────────────────────────────
int rightDepth = maxDepth(root->right);
// ─────────────────────────────────────────
// 第4步:返回较大深度 + 1
// max(leftDepth, rightDepth) 取较大的子树
// +1 是因为要加上当前根节点这一层
// ─────────────────────────────────────────
return max(leftDepth, rightDepth) + 1;
}
};
迭代版逐行解析
cpp
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0; // 空树直接返回0
queue<TreeNode*> q; // BFS用队列
q.push(root); // 根节点入队
int depth = 0; // 初始深度0
// ─────────────────────────────────────────
// 外层循环:一层层处理
// 队列为空说明所有节点都处理完了
// ─────────────────────────────────────────
while (!q.empty()) {
int size = q.size(); // 当前层的节点数量
// ─────────────────────────────────────────
// 内层循环:处理当前层的所有节点
// 把它们的子节点加入队列(下一层)
// ─────────────────────────────────────────
for (int i = 0; i < size; i++) {
TreeNode* node = q.front();
q.pop();
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
// ─────────────────────────────────────────
// 当前层处理完毕,深度+1
// ─────────────────────────────────────────
depth++;
}
return depth;
}
};
六、复杂度分析
时间复杂度
| 方法 | 分析 | 复杂度 |
|---|---|---|
| 递归 | 每个节点访问一次 | O(n) |
| 迭代 | 每个节点访问一次 | O(n) |
推导:n 个节点,每个节点只被访问一次。
空间复杂度
| 方法 | 分析 | 复杂度 |
|---|---|---|
| 递归 | 函数调用栈,最大深度为树高h | O(h) |
| 迭代 | 队列,最大宽度为最宽层的节点数 | O(n) |
递归推导:
- 最坏情况(链表形状):h = n,复杂度 O(n)
- 平衡树情况:h = log n,复杂度 O(log n)
迭代推导:
- 最宽层可能有 n/2 个节点,复杂度 O(n)
七、面试追问 FAQ
| 问题 | 回答 |
|---|---|
| 递归的思路是什么? | 后序遍历,左右子树深度取较大者,再+1 |
如何理解 +1? |
当前根节点算一层,所以左右子树深度取max后要+1 |
| 为什么递归比迭代空间复杂度低? | 递归栈深度是树高h,迭代队列宽度最大是n/2 |
| 如何求最小深度? | 改成 min(leftDepth, rightDepth),但要注意只有一边的特殊情况 |
| 最大深度和高度有什么区别? | 最大深度从根数到叶子(自顶向下),高度从叶子数到根(自底向上) |
八、相关题目
| 题目 | 难度 | 关键点 |
|---|---|---|
| 104. 二叉树的最大深度 | 简单 | 本题 |
| 111. 二叉树的最小深度 | 简单 | min + 特殊判断 |
| 110. 平衡二叉树 | 简单 | 最大深度 + 高度差判断 |
| 104. 二叉树的最大深度(迭代) | 简单 | BFS层序遍历 |
| 剑指 Offer 55-I. 二叉树的深度 | 简单 | 同本题 |
九、总结
| 对比项 | 递归 | 迭代 |
|---|---|---|
| 代码量 | 少(核心5行) | 较多(队列操作) |
| 时间复杂度 | O(n) | O(n) |
| 空间复杂度 | O(h) | O(n) |
| 适用场景 | 面试首选 | 需要直观层数时 |
核心公式:
最大深度 = max(左子树深度, 右子树深度) + 1