LeetCode 111简单二叉树
二叉树的最小深度 图解题解
这道题到底在问什么
给定一棵二叉树,求它的最小深度:从根到最近叶子的节点数。叶子 = 左右孩子都为空的节点。
- 输入
- [7,4,9,2,null,6,13,1,3,null,null,8,null,15]
- 输出
- 4
最优解:一步一步想明白
- 3这就是最小深度和最大深度最大的不同。最大深度里空侧取 0 无所谓(反正要 max);但最小深度求的是到最近叶子,空子树根本没有叶子,不能把它当成「0 步可达的叶子」。所以单孩子节点必须绕开空侧、走有孩子那边。下面逐节点看。
- 4后序 DFS 起点 = 根 7先看清这棵树:根是 7。树里有几个只有一个孩子的节点,它们不是叶子。接下来从根出发做后序 DFS——扎到底,叶子最小深度先定(都是 1),再一层层往上回传,单孩子节点只走有孩子那侧。
- 5visiting 7 → 先递归左右子树深度优先往下走,现在踩到节点 7,它左右孩子都在。后序遍历先把它挂在递归栈上(绿),等左右子树都算完,再取较浅的那侧 +1。
- 6visiting 4 → 先递归左右子树踩到节点 4,它只有左孩子,是个单孩子节点 —— 这正是本题最容易翻车的地方。它不是叶子,不能在这里收尾;缺的那侧是空,但空不代表「0 步就到叶子」,所以必须钻进有孩子的那一边继续找叶子。
- 7visiting 2 → 先递归左右子树深度优先往下走,现在踩到节点 2,它左右孩子都在。后序遍历先把它挂在递归栈上(绿),等左右子树都算完,再取较浅的那侧 +1。
- 8visiting 1 → 先递归左右子树走到叶子节点 1。它没有任何孩子,是一条路径的真正终点,所以它的最小深度 = 1。最小深度求的是「到最近叶子的距离」,而叶子必须是左右孩子都为空的节点。
- 91: 叶子 → 1节点 1 是叶子,最小深度直接定为 1。它先把这个 1 回传给父节点。
- 10visiting 3 → 先递归左右子树走到叶子节点 3。它没有任何孩子,是一条路径的真正终点,所以它的最小深度 = 1。最小深度求的是「到最近叶子的距离」,而叶子必须是左右孩子都为空的节点。
- 113: 叶子 → 1节点 3 是叶子,最小深度直接定为 1。它先把这个 1 回传给父节点。
- 122: min(1,1)+1 = 2节点 2 左右子树都回来了:左 1、右 1。两侧都通向叶子,取较浅的那个 +1,得到最小深度 = min(1,1)+1 = 2。算完标蓝(已定),回传给父节点。
- 134: 只有左(深度2) → 2+1 = 3节点 4 只有左孩子。右侧是空的,但绝不能拿右侧的 0 来算(那会得到 0+1=1,把它当成叶子)。最小深度只能沿有孩子的左侧走:左子树深度 2,所以它的深度 = 2+1 = 3。
- 14visiting 9 → 先递归左右子树深度优先往下走,现在踩到节点 9,它左右孩子都在。后序遍历先把它挂在递归栈上(绿),等左右子树都算完,再取较浅的那侧 +1。
- 15visiting 6 → 先递归左右子树踩到节点 6,它只有右孩子,是个单孩子节点 —— 这正是本题最容易翻车的地方。它不是叶子,不能在这里收尾;缺的那侧是空,但空不代表「0 步就到叶子」,所以必须钻进有孩子的那一边继续找叶子。
- 16visiting 8 → 先递归左右子树走到叶子节点 8。它没有任何孩子,是一条路径的真正终点,所以它的最小深度 = 1。最小深度求的是「到最近叶子的距离」,而叶子必须是左右孩子都为空的节点。
- 178: 叶子 → 1节点 8 是叶子,最小深度直接定为 1。它先把这个 1 回传给父节点。
- 186: 只有右(深度1) → 1+1 = 2节点 6 只有右孩子。左侧是空的,但不能用左侧的 0(否则算出 1,假装它是叶子,得到偏小的假深度)。最小深度必须沿有孩子的右侧走:右子树深度 1,所以它的深度 = 1+1 = 2。
- 19visiting 13 → 先递归左右子树踩到节点 13,它只有右孩子,是个单孩子节点 —— 这正是本题最容易翻车的地方。它不是叶子,不能在这里收尾;缺的那侧是空,但空不代表「0 步就到叶子」,所以必须钻进有孩子的那一边继续找叶子。
- 20visiting 15 → 先递归左右子树踩到节点 15,它只有右孩子,是个单孩子节点 —— 这正是本题最容易翻车的地方。它不是叶子,不能在这里收尾;缺的那侧是空,但空不代表「0 步就到叶子」,所以必须钻进有孩子的那一边继续找叶子。
- 21visiting 20 → 先递归左右子树走到叶子节点 20。它没有任何孩子,是一条路径的真正终点,所以它的最小深度 = 1。最小深度求的是「到最近叶子的距离」,而叶子必须是左右孩子都为空的节点。
- 2220: 叶子 → 1节点 20 是叶子,最小深度直接定为 1。它先把这个 1 回传给父节点。
- 2315: 只有右(深度1) → 1+1 = 2节点 15 只有右孩子。左侧是空的,但不能用左侧的 0(否则算出 1,假装它是叶子,得到偏小的假深度)。最小深度必须沿有孩子的右侧走:右子树深度 1,所以它的深度 = 1+1 = 2。
- 2413: 只有右(深度2) → 2+1 = 3节点 13 只有右孩子。左侧是空的,但不能用左侧的 0(否则算出 1,假装它是叶子,得到偏小的假深度)。最小深度必须沿有孩子的右侧走:右子树深度 2,所以它的深度 = 2+1 = 3。
- 259: min(2,3)+1 = 3节点 9 左右子树都回来了:左 2、右 3。两侧都通向叶子,取较浅的那个 +1,得到最小深度 = min(2,3)+1 = 3。算完标蓝(已定),回传给父节点。
- 267: min(3,3)+1 = 4节点 7 左右子树都回来了:左 3、右 3。两侧都通向叶子,取较浅的那个 +1,得到最小深度 = min(3,3)+1 = 4。算完标蓝(已定),回传给父节点。
- 27root.minDepth = min(3,3)+1 = 4回到根节点 7:左子树深度 3、右子树深度 3,取较浅的 +1 = 4。最短的一条路是 7→9→6→8 这 4 层 —— 它之所以是答案,正是因为单孩子节点 6 老老实实走了有孩子的那一侧,没有偷偷把空侧当叶子。
⚠️ 容易写错的地方
✗ 错:单孩子直接 min(左,右)+1
✓ 对:单孩子只走有孩子那侧 +1
空侧当 0 会算出偏小的假深度
✗ 错:把单孩子节点当叶子
✓ 对:叶子必须左右孩子都为空
半路的内部节点不是路径终点
✗ 错:空节点深度写成 1
✓ 对:空节点(null)深度是 0
这是递归出口,写错全盘皆错
完整代码(Java / Python / C++)
Java
class Solution {
public int minDepth(TreeNode root) {
if (root == null) return 0; // 空节点:深度 0(出口)
if (root.left == null && root.right == null)
return 1; // 叶子:深度 1
if (root.left == null) // 只有右孩子:必须走右
return minDepth(root.right) + 1;
if (root.right == null) // 只有左孩子:必须走左
return minDepth(root.left) + 1;
return Math.min(minDepth(root.left), // 两个孩子:取较浅 + 1
minDepth(root.right)) + 1;
}
}Python
class Solution:
def minDepth(self, root: TreeNode) -> int:
if root is None: # 空节点:深度 0
return 0
if root.left is None and root.right is None:
return 1 # 叶子:深度 1
if root.left is None: # 只有右孩子:必须走右
return self.minDepth(root.right) + 1
if root.right is None: # 只有左孩子:必须走左
return self.minDepth(root.left) + 1
return min(self.minDepth(root.left), # 两个孩子:取较浅 + 1
self.minDepth(root.right)) + 1C++
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == nullptr) return 0; // 空节点:深度 0
if (!root->left && !root->right)
return 1; // 叶子:深度 1
if (!root->left) // 只有右孩子:必须走右
return minDepth(root->right) + 1;
if (!root->right) // 只有左孩子:必须走左
return minDepth(root->left) + 1;
return min(minDepth(root->left), // 两个孩子:取较浅 + 1
minDepth(root->right)) + 1;
}
};复杂度
时间
O(n)
每个节点恰好访问一次
空间
O(h)
递归栈深 = 树高 h;最坏(链)O(n),平衡 O(log n)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 二叉树的最小深度 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
这道题为什么用「二叉树」,换最直接的暴力解会差在哪?+
二叉树抓住了本题的结构特征,把暴力解里重复的工作省掉;暴力解通常要多嵌套一层枚举,数据一大就超时。具体对比见上文「暴力解及其卡点」与「最优解逐步推演」两节。
时间复杂度为什么是 undefined?怎么推出来的?+
按上文复杂度小节的推导,时间复杂度为 undefined。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 二叉树的最小深度 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。