前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库二叉树 最大深度 高度 递归计算
算法算法树与遍历

如何求一棵二叉树的最大深度?

二叉树最大深度是指从根节点到最远叶子节点路径上的节点个数。常见的递归解法有自底向上合并子树深度和自顶向下逐层传递深度两种,结果一致。

前端进阶之旅 · 一题精讲更新于 2026.09.05
算法#树与遍历#测试
先看核心答案读代码示例
理解线索

递归深度计算的两种方向

  1. 自底向上后序处理左右子树后合并结果
  2. 自顶向下前序传递当前层数并更新最大
  3. 空节点基准null返回0,单节点返回1

根节点深度为1;若按边数定义则根深度为0,需提前确认。

核心回答

先记住这个答案

求二叉树最大深度即树高,按节点数计算。自底向上写法采用后序:若节点为空返回0,否则返回左右子树最大深度的较大值加1,代表根节点本身。自顶向下则维护当前深度变量,每向下一层加1,当节点为叶子或无孩子时用当前深度更新全局最大值。两者时间均为O(n),空间取决于树高,最差为链状O(n),平衡时为O(log n)。面试时需明确深度定义是节点数而非边数,空树深度为0。

  • 最大深度等效树高,按节点计数
  • 自底向上用后序返回子树高度
  • 自顶向下用前序维护当前深度

两种递归写法的核心差异

自底向上的递归式可写成 depth(null)=0,非空节点 depth(r)=max(depth(r.left),depth(r.right))+1。它模仿后序遍历,先探明孩子再决策当前,任何子树的计算结果独立。三节点左链的调用会依次返回1、2、3,每次把左结果+1。

自顶向下则把深度当作参数,调用时 traverse(node, level),下一层传 level+1。代码中每次访问节点都会用当前深度更新全局最大值(若更深则覆盖),即所有非空节点都触发比较。该遍历类似前序,可在递归入口就判断是否需先处理本节点,更利于携带路径前缀等额外信息。

两种递归求最大深度JavaScript
function maxDepthBottomUp(root) {
  if (root === null) return 0;
  return Math.max(maxDepthBottomUp(root.left), maxDepthBottomUp(root.right)) + 1;
}

let bestDepth = 0;
function topDownWalk(node, depth) {
  if (node === null) return;
  if (depth > bestDepth) bestDepth = depth;
  topDownWalk(node.left, depth + 1);
  topDownWalk(node.right, depth + 1);
}
function maxDepthTopDown(root) {
  bestDepth = 0;
  topDownWalk(root, 1);
  return bestDepth;
}

const n1 = { val: 1, left: null, right: null };
const leftChain = { val: 1, left: { val: 2, left: { val: 3, left: null, right: null }, right: null }, right: null };
const rightChain = { val: 1, left: null, right: { val: 2, left: null, right: { val: 3, left: null, right: null } } };
const balanced = { val: 1, left: { val: 2, left: { val: 4, left: null, right: null }, right: { val: 5, left: null, right: null } }, right: { val: 3, left: null, right: null } };

const cases = [
  { label: '空树', tree: null },
  { label: '单节点', tree: n1 },
  { label: '左链', tree: leftChain },
  { label: '右链', tree: rightChain },
  { label: '普通平衡树', tree: balanced }
];
for (const c of cases) {
  console.log(c.label + ': bottomUp=' + maxDepthBottomUp(c.tree) + ', topDown=' + maxDepthTopDown(c.tree));
}
查看输出与解释
空树: bottomUp=0, topDown=0
单节点: bottomUp=1, topDown=1
左链: bottomUp=3, topDown=3
右链: bottomUp=3, topDown=3
普通平衡树: bottomUp=3, topDown=3

代码定义两种递归函数,分别模拟后序合并与前序传递。测试覆盖空、单节点、左右链和普通树,输出完全一致。时间复杂度O(n),空间按树高变化。

统计招聘系统组织树的层级上限

假设某行政系统将组织架构以JSON树表示,每个部门节点包含子部门列表,需计算最大深度来决定页面是否直接展开全部。设输入树平均深度8层,最大可达40层,用自底向上递归对每个节点计算,一次遍历得到深度,前端据结果展示'展开'加深度提示。

因数据跑在Node.js服务端,默认递归上限约1万层,本场景最深仅几十层则安全。若预期极端情况(比如导入结构导致树退化为长链)可能很深,可先测量深度,超过阈值改用显式栈迭代,并在栈项中记录当前深度,从而避免爆栈且仍返回准确值。

容易出错的边界条件和处理策略

空树按0返回,因为不存在根节点,但若某些定义按边数则返回-1,差异会传染所有结果。斜树深度等于节点数,递归调用层数与高度相同,当n=10000时可能命中栈限制。此外,若单次调用重用自顶向下函数,忘记重置全局best变量会叠加历史最大值。

正确处理是在递归入口先处理空节点,再用后序或前序计算。对于深度极大场景,可改用显式栈,自顶向下可以存栈项(node,currentDepth);或先做层序遍历用队列记录层号。该代价是代码量增加,但空间是O(n)同样不可避免。

回答前,多想一步

容易答错的地方

混淆节点数与边数定义
部分面试官把深度定义为边数,此时根深度为0,空树应为-1。若不确认定义,自底向上写出的结果会偏大1,导致后续题目全部偏差。应在开头询问或明确说按节点数计算。
全局变量未重置
自顶向下实现常依赖一个外部best变量。如果一次测试后未重置,下一次调用的起点会沿用上次结果,空树也可能返回非0。正确做法是在入口先初始化,或使用局部变量通过返回值收集,减少隐式状态。
试着用自己的话回答

面试官还会怎么问?

如果要求返回最大深度对应的叶子节点,递归如何改?

自顶向下在每次到达叶子时比较当前深度与已知最大,若更深入则记录该leaf。自底向上则需返回(深度,叶子)二元组,合并时取较大深度对应的叶子。两种都能在O(n)内完成,但自底向上传递对象略繁琐。

二叉树用数组存储(堆式),如何求最大深度?

若按层序存放于数组,索引i的节点其子节点在2i+1和2i+2(从0起)。可递归模拟索引计算,但数组可能含空位,需判断索引越界或值是否空。复杂度仍O(n),但数组下标加深度可避免显式结构。

如何不用递归求最大深度?

使用显式栈模拟后序:每次弹出节点时已知其左右深度,取大的加1;也可用队列做层序遍历,每遍历一层深度+1。前者需要额外字段或双栈后序,后者直观但队列占用与最宽层成正比。

从一道题,走向一组知识

把知识连起来

树与遍历

如何根据前序和中序遍历结果重建一棵二叉树?

同属「树与遍历」专题,接着看 重建二叉树 前序中序 还原 在具体场景中的处理方式。

字符串算法

KMP 算法中的前缀函数(失败函数)是什么含义,如何在线性时间内求出?

继续了解 KMP 前缀函数 prefix function 计算,补充本题涉及的 算法 相关知识。

参考资料

  • Algorithms for Competitive Programming¶

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 两种递归写法的核心差异
  3. 统计招聘系统组织树的层级上限
  4. 容易出错的边界条件和处理策略
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑