GitHub_Trending/le/LeetCode-Book:二叉树中和为某一值的路径

GitHub_Trending/le/LeetCode-Book:二叉树中和为某一值的路径

【免费下载链接】LeetCode-Book 《剑指 Offer》 Python, Java, C++ 解题代码,LeetBook《图解算法数据结构》配套代码仓 【免费下载链接】LeetCode-Book 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Book

问题定义与核心挑战

二叉树路径求和问题(Path Sum)是算法面试中的高频考点,其核心要求是找到从根节点到叶节点的所有路径,使得路径上各节点值的总和等于目标值。该问题同时考察了二叉树的遍历技巧与路径记录能力,常见于《剑指 Offer》第34题与LeetBook《图解算法数据结构》LCR 153题。

问题特征分析

  • 结构约束:必须是从根节点到叶节点的完整路径(叶节点指左右子树均为空的节点)
  • 多解可能性:可能存在多条满足条件的路径,需返回所有有效路径集合
  • 边界条件:空树、单节点树、路径和为负数等特殊场景需特殊处理

算法设计:回溯法求解框架

核心思路

采用先序遍历+路径回溯的经典组合策略:

  1. 先序遍历:按"根→左→右"顺序遍历树结构,确保每条路径从根节点开始
  2. 路径记录:使用栈/列表实时维护当前路径,满足条件时保存路径副本
  3. 回溯机制:从子节点返回父节点前,移除当前节点值以恢复路径状态

算法流程图

mermaid

关键步骤解析

  1. 路径状态管理

    • 使用动态数组path存储当前路径
    • 节点访问时入栈(path.append),回溯时出栈(path.pop)
    • 满足条件时需创建路径副本(list(path))避免引用传递问题
  2. 目标值递减策略

    • 每次访问节点时从目标值中减去节点值
    • 叶节点处判断剩余目标值是否为0,避免重复求和计算

多语言实现代码对比

Python实现

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> list[list[int]]:
        result, path = [], []
        
        def backtrack(node: TreeNode, remaining: int) -> None:
            if not node:
                return
                
            # 加入当前节点到路径
            path.append(node.val)
            remaining -= node.val
            
            # 叶节点且路径和达标
            if not node.left and not node.right and remaining == 0:
                result.append(list(path))  # 关键: 创建副本而非引用
                
            # 递归遍历子树
            backtrack(node.left, remaining)
            backtrack(node.right, remaining)
            
            # 回溯: 移除当前节点
            path.pop()
            
        backtrack(root, targetSum)
        return result

Java实现

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {}
    TreeNode(int val) { this.val = val; }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        backtrack(root, targetSum);
        return result;
    }
    
    private void backtrack(TreeNode node, int remaining) {
        if (node == null) return;
        
        path.add(node.val);
        remaining -= node.val;
        
        // 叶节点判断需同时检查左右子树是否为空
        if (node.left == null && node.right == null && remaining == 0) {
            result.add(new LinkedList<>(path));  // 创建新列表避免引用问题
        }
        
        backtrack(node.left, remaining);
        backtrack(node.right, remaining);
        
        path.removeLast();  // 回溯操作
    }
}

C++实现

#include <vector>
using namespace std;

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    
    void backtrack(TreeNode* node, int remaining) {
        if (!node) return;
        
        path.push_back(node->val);
        remaining -= node->val;
        
        // 叶节点条件判断
        if (!node->left && !node->right && remaining == 0) {
            result.push_back(path);  // vector自动拷贝
        }
        
        backtrack(node->left, remaining);
        backtrack(node->right, remaining);
        
        path.pop_back();  // 回溯
    }
    
public:
    vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
        backtrack(root, targetSum);
        return result;
    }
};

复杂度分析与优化

基础复杂度

维度复杂度说明
时间复杂度O(N)需遍历所有N个节点
空间复杂度O(N)最差情况(链表)路径存储N个节点

优化方向

  1. 剪枝策略:当路径和超过目标值时提前终止(仅适用于非负权值树)
# 剪枝优化版本
def backtrack(node: TreeNode, remaining: int) -> None:
    if not node:
        return
    # 提前剪枝: 若当前值已超过剩余目标且节点值为正
    if node.val > remaining and node.val > 0:
        return
    # 后续逻辑同上...
  1. 迭代实现:使用栈模拟递归过程,避免递归栈溢出
# 迭代版实现
def pathSum(root: TreeNode, targetSum: int) -> list[list[int]]:
    if not root:
        return []
    result = []
    stack = [(root, targetSum - root.val, [root.val])]
    
    while stack:
        node, remaining, path = stack.pop()
        # 叶节点判断
        if not node.left and not node.right and remaining == 0:
            result.append(path)
        # 右子树先入栈(保证左子树先处理)
        if node.right:
            stack.append((node.right, remaining - node.right.val, path + [node.right.val]))
        if node.left:
            stack.append((node.left, remaining - node.left.val, path + [node.left.val]))
    
    return result

典型测试案例与易错点

测试用例设计

测试场景输入特征预期输出
标准二叉树根节点5,目标和22[[5,4,11,2],[5,8,4,5]]
单节点树节点值1,目标和1[[1]]
无有效路径根节点1,目标和2[]
包含负数节点节点值[-2,null,-3],目标和-5[[-2,-3]]
空树null,任意目标值[]

常见错误分析

  1. 路径拷贝问题

    • 错误:result.append(path)直接添加引用
    • 正确:result.append(list(path))创建副本
  2. 叶节点判断错误

    • 错误:仅判断node.left is Nonenode.right is None
    • 正确:需同时判断左右子树均为空not node.left and not node.right
  3. 回溯逻辑缺失

    • 错误:忘记在递归返回后执行path.pop()
    • 后果:路径包含已回溯节点,导致结果错误

实际应用与扩展问题

算法应用场景

  • 路径规划系统:寻找满足资源约束的路径
  • XML/JSON文档解析:提取满足特定条件的节点路径
  • 版本控制系统:查找提交历史中满足变更量要求的提交链

扩展问题变种

  1. 路径总和III:允许路径从任意节点开始,任意节点结束
  2. 二叉树最大路径和:寻找非空路径(不一定经过根节点)的最大和
  3. 路径求和IV:给定节点编号序列,计算路径和

学习资源与进阶建议

推荐练习题目

  • 基础:LeetCode 112.路径总和(判断是否存在有效路径)
  • 进阶:LeetCode 437.路径总和III(任意节点起始)
  • 挑战:LeetCode 124.二叉树中的最大路径和

参考资料

  • 《剑指Offer》第34题解析
  • LeetBook《图解算法数据结构》LCR 153题
  • 官方代码仓库:https://gitcode.com/GitHub_Trending/le/LeetCode-Book

总结与思考

二叉树路径求和问题展示了回溯算法在树形结构中的经典应用,核心在于路径状态的动态管理递归过程中的状态恢复。通过该问题的学习,可以深入理解:

  1. 先序遍历在路径探索中的引导作用
  2. 回溯法的"选择-探索-撤销"三步框架
  3. 复杂数据结构操作中的深拷贝与浅拷贝问题

建议通过不同解法的对比实现(递归vs迭代)、不同语言的特性差异(如Python列表引用与C++向量拷贝),加深对算法本质的理解,为解决更复杂的树路径问题奠定基础。

【免费下载链接】LeetCode-Book 《剑指 Offer》 Python, Java, C++ 解题代码,LeetBook《图解算法数据结构》配套代码仓 【免费下载链接】LeetCode-Book 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Book

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值