leetcode
leetcode 2801 ~ 2850
求和路径

求和路径

难度:

标签:

题目描述

You are given a binary tree in which each node contains an integer value (which might be positive or negative). Design an algorithm to count the number of paths that sum to a given value. The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).

Example:
Given the following tree and  sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1

Output:

3
Explanation: Paths that have sum 22 are: [5,4,11,2], [5,8,4,5], [4,11,7]

Note:

  • node number <= 10000

代码结果

运行时间: 30 ms, 内存: 17.0 MB


/*
 * 思路:
 * 1. 将树的所有路径转换为列表,并在每条路径中查找目标和。
 * 2. 使用递归方法获取从当前节点开始的所有路径。
 * 3. 使用Stream API计算每条路径的和,并过滤出等于目标和的路径。
 * 4. 返回符合条件的路径数量。
 */

import java.util.*;
import java.util.stream.*;

public class PathSumIIIStream {
    public int pathSum(TreeNode root, int sum) {
        if (root == null) return 0;
        List<List<Integer>> allPaths = getAllPaths(root);
        return (int) allPaths.stream()
                             .flatMap(Collection::stream)
                             .filter(path -> path.stream().mapToInt(Integer::intValue).sum() == sum)
                             .count();
    }
    
    private List<List<Integer>> getAllPaths(TreeNode node) {
        if (node == null) return Collections.emptyList();
        List<List<Integer>> paths = new ArrayList<>();
        if (node.left == null && node.right == null) {
            paths.add(new ArrayList<>(Collections.singletonList(node.val)));
            return paths;
        }
        for (List<Integer> path : getAllPaths(node.left)) {
            path.add(0, node.val);
            paths.add(path);
        }
        for (List<Integer> path : getAllPaths(node.right)) {
            path.add(0, node.val);
            paths.add(path);
        }
        return paths;
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

解释

方法:

该题解采用了前缀和的方法来高效地计算路径和。对于二叉树中的任意一条路径,可以通过维护一个从根节点到当前节点的路径和,以及一个哈希表来记录所有可能的前缀和及其出现的次数。对每个节点,计算当前的路径和,并查询哈希表中路径和减去目标值(sum)的结果出现的次数,这些次数即为以当前节点结束的、和为目标值的路径数量。在遍历每个节点后,更新哈希表以包含当前路径和的次数,并在返回前减少当前路径和的计数,以防影响其他路径的计算。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在哈希表中初始需要设置prefix[0]=1?这代表了什么含义?
在哈希表中初始设置prefix[0] = 1是为了处理从根节点开始的路径正好等于目标和的情况。这个初始化意味着存在一个虚拟的前缀和为0,它代表在任何实际节点之前,没有任何节点的和。因此,当某个节点的累计和等于目标和时,它可以被视为从根节点到该节点的路径和等于目标和,而这条路径的前缀和与'0'的差也恰好等于目标和。
🦆
在函数path中,prefix[total-sum]的查询是如何确保找到的路径是向下且不跨越非父子节点的?
在函数path中,使用prefix[total-sum]的查询确保找到的路径是向下且不跨越非父子节点的,这是因为前缀和的更新和查询都是在单个从根到叶的递归遍历过程中进行的。只有当前遍历的路径的节点会影响当前的路径和total,并且prefix哈希表是实时更新的,每次递归返回前会将当前节点的路径和计数减少,确保不会影响其他分支的计算。这样,每次查询prefix[total - sum]时,它反映的是当前节点的祖先节点到当前节点的路径和,且这些路径都是从上到下的。
🦆
在递归函数path中,对prefix的更新操作为何在递归调用前后都有执行,这种操作的目的是什么?
在递归函数path中,对prefix的更新操作在递归调用前后都执行,这是为了正确管理路径和的计数,以便能够准确地计算符合条件的路径数量。在递归调用之前,需要增加当前路径和的计数,这是因为当前节点已被纳入路径和中。而递归调用之后,需要减少当前路径和的计数是为了进行回溯,这确保了当前节点的路径和不会影响到其它可能的路径计算。这种'增加-递归-减少'的模式是为了保证每个节点在递归搜索过程中只影响它所在路径的计算,不会错误地影响其他路径。

相关问题