1. 二叉树深度优先搜索基础解析
深度优先搜索(DFS)是二叉树算法中最基础也最核心的遍历方式之一。与广度优先搜索(BFS)按层级遍历不同,DFS会沿着一条路径尽可能深入地探索,直到到达叶子节点再回溯。这种特性使其特别适合解决需要穷尽所有可能路径的问题。
在二叉树场景中,DFS有三种经典实现方式:
- 前序遍历(根-左-右)
- 中序遍历(左-根-右)
- 后序遍历(左-右-根)
递归是实现DFS最直观的方式,其核心在于将大问题分解为相同结构的子问题。以计算二叉树节点总数为例:
python复制def count_nodes(root):
if not root: # 基线条件
return 0
return 1 + count_nodes(root.left) + count_nodes(root.right) # 递归分解
这个简单例子展示了递归三要素:
- 基线条件(空节点返回0)
- 递归调用(处理左右子树)
- 问题分解(总数=1+左子树数+右子树数)
提示:递归代码虽然简洁,但需要注意栈溢出风险。对于深度很大的二叉树,迭代实现(使用显式栈)可能更安全。
2. 问题6:计算布尔二叉树的值
2.1 问题描述与建模
给定一个完整布尔二叉树(每个非叶子节点有2个子节点),其中:
- 叶子节点值为0(False)或1(True)
- 非叶子节点值为2(逻辑OR)或3(逻辑AND)
要求计算根节点表示的真值。例如:
code复制 3
/ \
2 0
/ \
1 0
应返回0(因为AND(OR(1,0),0) = 0)
2.2 递归解法实现
采用后序遍历(左-右-根)可以自然处理这种依赖子节点结果的问题:
python复制def evaluate_tree(root):
if root.val in {0, 1}: # 叶子节点直接返回值
return root.val
left_val = evaluate_tree(root.left)
right_val = evaluate_tree(root.right)
if root.val == 2: # OR运算
return left_val or right_val
else: # AND运算
return left_val and right_val
时间复杂度:O(N),每个节点访问一次
空间复杂度:O(H),递归栈深度与树高H相关
2.3 关键点与边界处理
- 节点值验证:题目保证输入合法,但实际工程中应验证节点值是否在{0,1,2,3}内
- 非完整树处理:虽然题目限定完整二叉树,但可扩展处理:
python复制if not root.left and not root.right: # 更通用的叶子节点判断
return root.val
- 短路优化:对于AND运算,左子树为0时可提前返回;OR运算左子树为1时同理
3. 问题7:求根到叶节点数字之和
3.1 问题场景分析
给定一个二叉树,每个节点存储一位数字(0-9),从根到叶节点的路径组成一个多位数。求所有路径数字之和。例如:
code复制 1
/ \
2 3
路径"12"和"13"的和为25。
这类路径求和问题通常需要:
- 维护当前路径值
- 在叶子节点进行累加
- 回溯时撤销状态更新
3.2 深度优先搜索实现
使用前序遍历可以在访问节点时即时更新路径值:
python复制def sum_numbers(root):
def dfs(node, path_sum):
if not node:
return 0
path_sum = path_sum * 10 + node.val
if not node.left and not node.right: # 叶子节点
return path_sum
return dfs(node.left, path_sum) + dfs(node.right, path_sum)
return dfs(root, 0)
3.3 迭代解法对比
递归虽然简洁,但迭代方案有时更易理解且避免栈溢出:
python复制def sum_numbers_iter(root):
if not root:
return 0
total = 0
stack = [(root, root.val)]
while stack:
node, curr_sum = stack.pop()
if not node.left and not node.right:
total += curr_sum
continue
if node.right:
stack.append((node.right, curr_sum * 10 + node.right.val))
if node.left:
stack.append((node.left, curr_sum * 10 + node.left.val))
return total
两种实现的时间复杂度均为O(N),但迭代方式的空间复杂度最坏情况下会更高(当树退化为链表时)。
4. 深度优先搜索的优化技巧
4.1 记忆化搜索应用
对于存在重复计算的DFS问题(如斐波那契数列、二叉树路径问题),可以使用记忆化存储中间结果。例如在求二叉树最大路径和时:
python复制def max_path_sum(root):
memo = {}
def dfs(node):
if not node:
return 0
if node in memo: # 查缓存
return memo[node]
left = max(dfs(node.left), 0)
right = max(dfs(node.right), 0)
memo[node] = max(left, right) + node.val # 存储结果
return memo[node]
dfs(root)
return max(memo.values())
4.2 剪枝策略
在搜索过程中提前终止不可能产生最优解的分支。例如在验证二叉搜索树时:
python复制def is_valid_bst(root):
def dfs(node, lower=float('-inf'), upper=float('inf')):
if not node:
return True
if node.val <= lower or node.val >= upper: # 不满足BST条件
return False
return (dfs(node.left, lower, node.val) and
dfs(node.right, node.val, upper))
return dfs(root)
4.3 非递归实现模板
深度优先搜索的迭代模板适用于大多数场景:
python复制def dfs_iterative(root):
if not root:
return
stack = [root]
visited = set()
while stack:
node = stack.pop()
if node in visited:
continue
visited.add(node)
# 处理当前节点
print(node.val)
# 注意压栈顺序(保证左子树先处理)
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
5. 常见问题与调试技巧
5.1 递归深度过大
当二叉树高度超过1000时,Python默认递归深度可能引发栈溢出。解决方案:
- 改用迭代实现
- 调整递归深度限制(不推荐)
python复制import sys
sys.setrecursionlimit(100000)
5.2 路径记录问题
在需要记录完整路径的场景(如打印所有根到叶路径),注意列表的深拷贝:
python复制def binary_tree_paths(root):
def dfs(node, path, res):
if not node:
return
path.append(str(node.val))
if not node.left and not node.right:
res.append('->'.join(path))
else:
dfs(node.left, path.copy(), res) # 关键:创建path副本
dfs(node.right, path.copy(), res)
res = []
dfs(root, [], res)
return res
5.3 空节点处理
二叉树问题中空节点处理是常见错误源。建议统一采用:
python复制if not node:
# 根据问题需求返回0、None或[]
return default_value
5.4 测试用例设计
全面的测试用例应包含:
- 空树
- 单节点树
- 完全二叉树
- 倾斜树(左斜或右斜)
- 随机生成的普通树
例如对问题7的测试案例:
python复制def test_sum_numbers():
# 空树
assert sum_numbers(None) == 0
# 单节点
root = TreeNode(1)
assert sum_numbers(root) == 1
# 示例树
root = TreeNode(1, TreeNode(2), TreeNode(3))
assert sum_numbers(root) == 25
# 复杂树
root = TreeNode(4, TreeNode(9, TreeNode(5), TreeNode(1)), TreeNode(0))
assert sum_numbers(root) == 1026