1. 二叉树深度优先搜索算法精讲
最近在整理算法教学材料时,发现很多同学对二叉树DFS的应用场景理解不够深入。今天我们就通过两道经典力扣题目(2331.计算布尔二叉树的值、129.求根节点到叶节点数字之和),来拆解DFS在二叉树问题中的实战技巧。
作为基础算法中的"三剑客"之一,深度优先搜索在树形结构处理中展现出了独特的优势。特别是在处理需要完全遍历路径的问题时,DFS凭借其"一条路走到黑"的特性,往往能写出更简洁优雅的解法。下面我将结合这两道题目,分享DFS在二叉树问题中的标准解题框架和优化技巧。
2. 题目解析与算法设计
2.1 布尔二叉树求值(力扣2331)
题目要求我们计算一个特殊二叉树的布尔值:
- 叶子节点值为0或1(对应false/true)
- 非叶子节点值为2或3(对应OR/AND运算)
python复制class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
2.1.1 递归终止条件分析
当遇到叶子节点时直接返回其布尔值:
python复制if not root.left and not root.right:
return root.val == 1
2.1.2 递归过程设计
根据节点类型进行不同运算:
python复制left = evaluateTree(root.left)
right = evaluateTree(root.right)
if root.val == 2: # OR
return left or right
else: # AND
return left and right
2.2 根到叶节点数字和(力扣129)
这道题需要将所有根到叶的路径转换为数字后求和。例如:
code复制 1
/ \
2 3
路径12和13的和为25。
2.2.1 路径累积技巧
使用参数传递当前路径值:
python复制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)
2.2.2 时空复杂度优化
- 时间复杂度:O(n) 每个节点访问一次
- 空间复杂度:O(h) 递归栈深度为树高
3. DFS解题框架详解
3.1 通用递归模板
python复制def dfs(node):
# 终止条件
if not node:
return base_case
# 处理当前节点
process(node)
# 递归子节点
left = dfs(node.left)
right = dfs(node.right)
# 合并结果
return combine(left, right)
3.2 参数设计原则
- 必须参数:当前节点
- 可选参数:
- 路径信息(如累计和)
- 全局变量(如结果集)
- 状态标记(如是否已访问)
3.3 四种常见变体
- 前序处理:先处理当前节点
- 中序处理:左→中→右
- 后序处理:先处理子树(如布尔树求值)
- 带状态回溯:需要撤销选择时使用
4. 实战优化技巧
4.1 记忆化搜索
对于重复子问题,使用缓存提升效率:
python复制from functools import lru_cache
@lru_cache(maxsize=None)
def dfs(node):
# ...原有逻辑
4.2 尾递归优化
某些语言支持尾递归优化,可减少栈空间使用:
python复制def dfs(node, acc, result):
if not node:
return result
# ...处理逻辑
return dfs(child, new_acc, new_result)
4.3 迭代实现DFS
使用显式栈避免递归开销:
python复制def dfs_iterative(root):
stack = [(root, 0)]
total = 0
while stack:
node, path_sum = stack.pop()
# ...处理逻辑
if node.right:
stack.append((node.right, new_sum))
if node.left:
stack.append((node.left, new_sum))
return total
5. 常见错误排查
5.1 终止条件缺失
典型症状:栈溢出或错误结果
解决方法:确保所有边界条件都被覆盖
5.2 参数传递错误
案例:忘记在递归调用时更新路径值
python复制# 错误写法
dfs(node.left, path_sum) # 没有用新值覆盖
# 正确写法
path_sum = path_sum * 10 + node.val
5.3 状态污染
当使用可变对象作为参数时:
python复制def dfs(node, path=[]): # 危险!默认参数是共享的
path.append(node.val) # 会影响其他递归分支
6. 进阶训练建议
-
路径类问题:
- 113.路径总和II
- 437.路径总和III
-
属性计算问题:
- 543.二叉树的直径
- 124.二叉树中的最大路径和
-
构造类问题:
- 105.从前序与中序遍历序列构造二叉树
- 106.从中序与后序遍历序列构造二叉树
对于想系统提升树形问题解决能力的同学,建议按照"基础遍历→路径处理→属性计算→复杂构造"的顺序循序渐进。每道题至少尝试三种写法:递归DFS、迭代DFS、BFS,比较它们的适用场景。