hot100 之104-二叉树的最大深度(递归+二叉树)
今日算法题:
题目
题解
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):def maxDepth(self, root):if not root:return 0left_depth = self.maxDepth(root.left)right_depth = self.maxDepth(root.right)return 1 + max(left_depth,right_depth)
核心思路
语法小贴士
if not root:
:在 Python 里,None
被当作“假”,所以“如果没有节点就返回 0”。函数调用自己就叫递归:
self.maxDepth(root.left)
。max(a, b)
取较大值。return
返回该子树的最大深度给“上一层”调用者。
这段递归到底怎么“跑”的?(以根=3为例)
去算
3
的左子树(根=9):9 的左是空 → 0;右是空 → 0;所以 9 的深度 =
1 + max(0,0) = 1
去算
3
的右子树(根=20):20 的左(15)深度 =
1
;右(7)深度 =1
;所以 20 的深度 =1 + max(1,1) = 2
最后
3
的深度 =1 + max(1,2) = 3
✅
该方法时间复杂度是 O(n)(每个节点访问一次)。
空间复杂度:
递归:最坏 O(h)(h=树高,最坏退化链表时 O(n))。
体现知识点
二叉树节点实现
力扣里通常给这个结构(或等价):
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = val # 当前节点存的数self.left = left # 左孩子(是另一个 TreeNode 或 None)self.right = right # 右孩子(是另一个 TreeNode 或 None)
None
表示“没有孩子”。链接关系靠
left
和right
这两个“引用变量”落实:比如root.left = node9
就把根的左指针指向node9
。
那么这段代码怎么就能直接表示成一棵二叉树呢?
我并没有任何手动的代码实现,我只能看到测试用例得有一串数组,直接看代码是无法变成树的呀?也就无法调用这些left
、right
的方法实现树的左子树右子树的调用。
答案是:
LeetCode/测试平台帮你做了
在 LeetCode 上,题目输入通常是 [3,9,20,null,null,15,7]
这样的数组。
👉 平台在你代码跑之前,会先把输入数组“转成一棵二叉树”,这就是测试用例的底层逻辑。
它内部其实就是写了类似的代码:
# 假设输入是 [3,9,20,null,null,15,7]
root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)
只是这些赋值的步骤你看不到,是平台帮你造好了这棵树,然后把 root
传给你的函数。
所以你在函数里直接用 root
,就能拿到已经链接好的二叉树。
平台(LeetCode)的情况
你在 解题函数 里拿到的参数是
root: TreeNode
,它已经是一棵二叉树的根节点。之所以能这样,是因为 LeetCode 在测试用例的底层,把输入的数组
[3,9,20,null,null,15,7]
转换成了一棵树,然后才把树的root
传给你的函数。所以你才会感觉“我啥都没写,怎么树就有了”。
👉 本质上是 平台替你写好了建树的逻辑。
本地自己写代码的情况
如果你直接在本地写:
def maxDepth(root):# 求树的最大深度...root = [3,9,20,None,None,15,7] # 直接传数组
print(maxDepth(root))
❌ 这是不行的。
因为 root
只是一个 Python 列表,根本不是 TreeNode
对象,更没有 .left
、.right
这些属性。
你调用 root.left
会直接报错:AttributeError: 'list' object has no attribute 'left'
。
若要在本地执行正确做法
如果你在本地写,就要自己实现“数组 → 二叉树”的过程(LeetCode 帮你隐藏的那一步)。
比如用我刚才给的 build_tree
函数:
class TreeNode:def __init__(self, val=0, left=None, right=None):self.val = valself.left = leftself.right = rightfrom collections import dequedef build_tree(values):if not values:return Noneroot = TreeNode(values[0])queue = deque([root])i = 1while queue and i < len(values):node = queue.popleft()if values[i] is not None:node.left = TreeNode(values[i])queue.append(node.left)i += 1if i < len(values) and values[i] is not None:node.right = TreeNode(values[i])queue.append(node.right)i += 1return root# 本地用数组造树
root = build_tree([3,9,20,None,None,15,7])
这样 root
就是一个真正的二叉树对象了,接下来你写的 maxDepth(root)
才能跑。
过程困难
逻辑问题:
就是上面二叉树的理解
优化解法
还有一种解法是用层序遍历的方法解,但是使用递归的方法易于理解也易于实现。
BFS(队列)版本——逐行解释
from collections import deque # deque 是高效队列class Solution:def maxDepth(self, root: 'TreeNode') -> int:if not root:return 0queue = deque([root]) # 初始队列放入根depth = 0 # 已经走过的层数while queue: # 只要这一层还有节点depth += 1 # 新的一层开始了for _ in range(len(queue)): # 把“当前层”的节点个数先记下来node = queue.popleft() # 逐个取出这一层的节点if node.left:queue.append(node.left) # 把下一层的孩子放进去if node.right:queue.append(node.right)return depth
语法小贴士
deque([...])
:创建一个队列,popleft()
从左边弹出,表示“出队”。while queue:
:队列非空就继续。for _ in range(len(queue)):
:这一招很关键!len(queue)
是“当前层”的节点数;用它控制 for 循环次数,确保这一轮只处理这一层;
_
表示“这个变量我不打算用”。
按层走一遍(你的例子)
队列:[3] → 处理 1 个节点(3),加入它的孩子 9、20;深度=1
队列:[9,20] → 处理 2 个节点(9、20),加入 20 的孩子 15、7;深度=2
队列:[15,7] → 处理 2 个节点(15、7),没有孩子可加;深度=3
队列空 → 结束,返回 3 ✅
复杂度与选择
两种方法时间复杂度都是 O(n)(每个节点访问一次)。
空间复杂度:
递归:最坏 O(h)(h=树高,最坏退化链表时 O(n))。
BFS:最坏 O(w)(w=某一层的最大节点数,满二叉树中部层最宽)。
怎么选?
代码简洁→选递归;
想按层处理或不想用递归→选 BFS。