二元树路径求和问题的解决方案
问题分析
- 目标:从根节点到叶节点的路径中,找出所有节点值之和等于目标整数的路径。
- 关键约束:路径必须是从根到叶的完整路径(叶节点无左右子树)。
核心算法:深度优先搜索(DFS)
思路
- 递归遍历:从根节点出发,递归探索左右子树。
- 路径跟踪:用栈或列表记录当前路径的节点值。
- 和校验:到达叶节点时,检查路径和是否等于目标值,若相等则输出路径。
步骤示例(以目标和 22 为例):
路径 1:10 → 12 | |
- 路径和:10 + 12 = 22 → 匹配目标,输出。 | |
路径 2:10 → 5 → 7 | |
- 路径和:10 + 5 + 7 = 22 → 匹配目标,输出。 |
实现要点
- 递归参数:当前节点、当前路径列表、当前路径和。
- 终止条件:
- 节点为空 → 直接返回。
- 叶节点 → 校验路径和,匹配则打印路径。
- 回溯处理:递归返回后弹出当前节点(恢复路径状态,避免影响其他分支)。
代码实现(Python)
python
class TreeNode: | |
def __init__(self, val=0, left=None, right=None): | |
self.val = val | |
self.left = left | |
self.right = right | |
def find_path(root, target_sum): | |
result = [] | |
def dfs(node, current_path, current_sum): | |
if not node: | |
return | |
# 更新路径和与当前路径 | |
current_sum += node.val | |
current_path.append(node.val) | |
# 叶节点且和匹配 | |
if not node.left and not node.right and current_sum == target_sum: | |
result.append(list(current_path)) # 深拷贝路径 | |
# 递归左右子树 | |
dfs(node.left, current_path, current_sum) | |
dfs(node.right, current_path, current_sum) | |
# 回溯:移除当前节点 | |
current_path.pop() | |
dfs(root, [], 0) | |
return result | |
# 示例树构建 | |
root = TreeNode(10) | |
root.left = TreeNode(5, TreeNode(4), TreeNode(7)) | |
root.right = TreeNode(12) | |
# 测试:目标和 22 | |
print(find_path(root, 22)) # 输出:[[10, 5, 7], [10, 12]] |
复杂度分析
- 时间复杂度:O(n),每个节点仅访问一次。
- 空间复杂度:O(h),h 为树的高度(递归栈深度,最坏情况为链状树 O(n))。
扩展场景
- 路径不限制根到叶:需允许从任意节点开始/结束(需修改递归逻辑,取消叶节点限制)。
- 二叉搜索树(BST)优化:利用 BST 特性剪枝(如当前和超过目标时停止右子树搜索)。
- 记录路径坐标:扩展存储节点索引或指针,便于定位具体节点。
该方法通过深度优先搜索与回溯结合,高效遍历所有可能路径,是解决树路径求和问题的经典方案。
在二叉搜索树(BST)中进行路径求和剪枝时,可利用 左子树所有节点值 ≤ 当前节点值 ≤ 右子树所有节点值 的特性优化搜索。以下是具体实现:
核心剪枝逻辑
- 提前终止右子树搜索:若当前路径和 + 当前节点值 已超过目标值,则右子树所有路径和必然更大(因右子树节点值 ≥ 当前节点值),可直接剪枝。
- 保留左子树搜索:左子树节点值更小,可能存在和为目标值的路径,需继续递归。
代码实现(Python)
python
class TreeNode: | |
def __init__(self, val=0, left=None, right=None): | |
self.val = val | |
self.left = left | |
self.right = right | |
def find_bst_path(root, target_sum): | |
result = [] | |
def dfs(node, current_path, current_sum): | |
if not node: | |
return | |
current_sum += node.val | |
current_path.append(node.val) | |
# 叶节点且和匹配:记录路径 | |
if not node.left and not node.right and current_sum == target_sum: | |
result.append(list(current_path)) | |
# 剪枝逻辑:仅当当前和 <= 目标值时,才搜索右子树 | |
if current_sum <= target_sum: | |
dfs(node.right, current_path, current_sum) # 右子树可能存在更大值但和仍达标 | |
# 左子树始终需要搜索(节点值更小,可能存在和为目标的路径) | |
dfs(node.left, current_path, current_sum) | |
# 回溯:移除当前节点 | |
current_path.pop() | |
dfs(root, [], 0) | |
return result | |
# 示例 BST 构建(满足左 ≤ 中 ≤ 右) | |
root = TreeNode(10) | |
root.left = TreeNode(5, TreeNode(4), TreeNode(7)) # 左子树:5 (4,7) | |
root.right = TreeNode(12) # 右子树:12 | |
# 测试:目标和 22 | |
print(find_bst_path(root, 22)) # 输出:[[10, 5, 7], [10, 12]] |
剪枝效果说明
- 右子树剪枝:例如当前路径和为
10 + 12 = 22
(已等于目标),此时右子树为空,无需处理;若目标和为20
,则10 + 12 = 22 > 20
,直接剪枝右子树,避免无效递归。 - 左子树不剪枝:例如目标和为
19
,当前路径10 + 5 = 15 ≤ 19
,需继续搜索左子树的4
和7
(如10 + 5 + 4 = 19
为有效路径)。
关键优化点
- 剪枝条件:
current_sum <= target_sum
是右子树搜索的前提,利用 BST 右子树递增特性避免无效计算。 - 与普通二叉树对比:普通二叉树需搜索所有路径(O(n)),而 BST 在目标值较小时可显著减少右子树遍历次数(最坏情况仍为 O(n),但平均效率提升)。
通过此剪枝策略,可在 BST 路径求和问题中有效减少不必要的递归,提升搜索效率。