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 0
        left_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 表示"没有孩子"。

  • 链接关系靠 leftright 这两个"引用变量"落实:比如 root.left = node9 就把根的左指针指向 node9

那么这段代码怎么就能直接表示成一棵二叉树呢?

我并没有任何手动的代码实现,我只能看到测试用例得有一串数组 ,直接看代码是无法变成树的呀?也就无法调用这些leftright的方法实现树的左子树右子树的调用。

答案是:

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 = val
        self.left = left
        self.right = right

from collections import deque

def build_tree(values):
    if not values:
        return None
    
    root = TreeNode(values[0])
    queue = deque([root])
    i = 1

    while queue and i < len(values):
        node = queue.popleft()
        if values[i] is not None:
            node.left = TreeNode(values[i])
            queue.append(node.left)
        i += 1
        if i < len(values) and values[i] is not None:
            node.right = TreeNode(values[i])
            queue.append(node.right)
        i += 1
    return 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 0

        queue = 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。