444_二叉树面试题精选¶
一句话说明¶
二叉树面试题 80% 靠递归,套路是"当前节点做什么 + 递归左右子树",先序/中序/后序决定处理时机,层序用 BFS 队列。
核心知识点¶
白话理解¶
二叉树就像家谱——一个祖先,每人最多两个后代。面试题几乎都是"问一个节点的事,答案由左边和右边拼出来"。递归就是:处理当前节点 → 让左子树递归 → 让右子树递归,三步走。
三种遍历顺序¶
经典题目与解法¶
题目1:二叉树的最大深度(LeetCode 104)¶
题意:给定二叉树,返回最大深度(从根到最远叶子节点的层数)。
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def max_depth(root) -> int:
"""
递归思路:当前节点的深度 = 1 + max(左子树深度, 右子树深度)
base case:空节点深度为0
"""
if not root: # 空节点,深度为0
return 0
left_depth = max_depth(root.left) # 左子树最大深度
right_depth = max_depth(root.right) # 右子树最大深度
return 1 + max(left_depth, right_depth) # 当前节点深度=子树最大+1
# BFS 迭代法(层序遍历,每层+1)
from collections import deque
def max_depth_bfs(root) -> int:
if not root:
return 0
queue = deque([root]) # 从根节点开始
depth = 0
while queue:
depth += 1 # 每处理一层,深度+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
时间复杂度:O(n) 访问每个节点一次
空间复杂度:O(h) h 是树高,最坏 O(n)
题目2:验证二叉搜索树(LeetCode 98)¶
题意:判断一棵二叉树是否是有效的 BST(左子树所有值 < 根 < 右子树所有值)。
def is_valid_bst(root) -> bool:
"""
思路:递归传入合法范围 [min_val, max_val]
每个节点的值必须在这个范围内
往左走时,上界更新为当前值;往右走时,下界更新为当前值
"""
def validate(node, min_val, max_val):
if not node: # 空节点默认合法
return True
# 当前节点值必须严格在 (min_val, max_val) 范围内
if node.val <= min_val or node.val >= max_val:
return False
# 递归左子树:左子树所有值必须 < node.val(更新上界)
# 递归右子树:右子树所有值必须 > node.val(更新下界)
return (validate(node.left, min_val, node.val) and
validate(node.right, node.val, max_val))
return validate(root, float('-inf'), float('inf')) # 初始范围无穷大
# 中序遍历法:BST中序遍历结果必须是严格递增序列
def is_valid_bst_inorder(root) -> bool:
prev = float('-inf') # 记录上一个访问的值
def inorder(node):
nonlocal prev
if not node:
return True
if not inorder(node.left): # 先检查左子树
return False
if node.val <= prev: # 当前值必须严格大于前一个
return False
prev = node.val # 更新前一个值
return inorder(node.right) # 再检查右子树
return inorder(root)
时间复杂度:O(n)
空间复杂度:O(h)
题目3:二叉树的层序遍历(LeetCode 102,BFS 模板)¶
题意:按层返回二叉树的节点值,每层一个列表。
def level_order(root):
"""
BFS:用队列按层处理
关键:每层开始时记录队列长度,只处理这么多节点(当前层)
"""
if not root:
return []
result = [] # 存每层的结果
queue = deque([root]) # 初始只有根节点
while queue:
level_size = len(queue) # 当前层的节点数
level_vals = [] # 当前层的值
for _ in range(level_size): # 只处理当前层
node = queue.popleft()
level_vals.append(node.val)
if node.left: # 下一层的节点加入队列
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level_vals) # 当前层处理完毕
return result
# 测试
root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)
print(level_order(root)) # [[3], [9, 20], [15, 7]]
时间复杂度:O(n)
空间复杂度:O(w) w 是最大宽度(最宽一层的节点数)
题目4:二叉树的最近公共祖先(LeetCode 236)¶
def lowest_common_ancestor(root, p, q):
"""
后序遍历:先递归左右,再在当前节点做判断
如果左右各找到一个,则当前节点就是 LCA
"""
if not root or root == p or root == q: # 找到目标节点或到底
return root
left = lowest_common_ancestor(root.left, p, q) # 左子树找
right = lowest_common_ancestor(root.right, p, q) # 右子树找
if left and right: # 左右各找到一个,当前节点是LCA
return root
return left if left else right # 只有一侧找到,返回那侧
面试技巧¶
- 递归三要素:终止条件、当前层做什么、递归子问题——先说清楚再写代码
- 后序用于"汇总子树信息":当前节点的答案依赖子树结果时用后序
- BST 记住中序递增:验证 BST、第 k 小元素都用中序
- BFS 模板背熟:
while queue: for _ in range(len(queue)): ...这个两层循环 - 画图再下手:树题比链表更需要先画图
速查表¶
# 前序遍历(递归)
def preorder(node):
if not node: return
print(node.val) # 处理当前
preorder(node.left)
preorder(node.right)
# 中序遍历(递归)
def inorder(node):
if not node: return
inorder(node.left)
print(node.val) # 处理当前(BST中是升序)
inorder(node.right)
# 后序遍历(递归)
def postorder(node):
if not node: return
postorder(node.left)
postorder(node.right)
print(node.val) # 处理当前(最后处理自己)
# 层序遍历(BFS,必背)
queue = deque([root])
while queue:
size = len(queue)
for _ in range(size):
node = queue.popleft()
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
| 题型 | 遍历方式 | 例题 |
|---|---|---|
| 最大深度/高度 | 后序(汇总) | LeetCode 104 |
| 验证BST | 中序或带范围递归 | LeetCode 98 |
| 层序遍历 | BFS | LeetCode 102 |
| 最近公共祖先 | 后序 | LeetCode 236 |
| 路径总和 | 前序(累加路径) | LeetCode 112 |