求和路径

题目描述

来源于 https://leetcode-cn.com/

给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。

示例:
给定如下二叉树,以及目标和 sum = 22

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

返回:

3
解释:和为 22 的路径有:[5,4,11,2], [5,8,4,5], [4,11,7]

提示:

  • 节点总数 <= 10000

解法一:递归

遍历每个节点,从当前节点开始向下求和。下面的函数中,pathSum 完成对节点的遍历,traversal 中进行求和。

class Solution {
public:
    int pathSum(TreeNode* root, int sum) {
        if(root == 0){
            return 0;
        }
        int num = traversal(root, sum);
        num += pathSum(root->left, sum);
        num += pathSum(root->right, sum);
        return num;
    }

private:
    int traversal(TreeNode* node, int target){
        if(!node) return 0;
        int num = 0;
        target -= node->val;

        if(0 == target){
            num += 1;
        }
        num += traversal(node->left, target);
        num += traversal(node->right, target);
        return num;
    }
};

解法二:前缀和

在一个序列中,一个值及其之前的所有值的和,叫做前缀和。从根节点到达某个节点的路径为一个序列,该序列上的每个节点,都有一个前缀和。如果某个靠后的节点的前缀和减去前面的一个前缀和的值为 n,那么说明两个节点间的所有节点的和为 n。

基于此想法,在深度遍历的时候,记录下每个节点的前缀和,构成一个表,key 为前缀和,value 为有此前缀和的数量。然后用当前节点的前缀和减去 sum 得到 n,用 n 去表中查询,如果能够找到,说明必然以之前的某个点为起点,当前点为终点构成的路径之和为 sum。

class Solution {
public:
    int pathSum(TreeNode* root, int sum) {
        unordered_map<int, int> prefix_sum_to_count_map;
        prefix_sum_to_count_map[0] = 1;

        int num = traversal(root, prefix_sum_to_count_map, 0, sum);
        return num;
    }

private:
    int traversal(TreeNode* node, unordered_map<int, int>& mp, int sum, int target){
        if(!node) return 0;
        sum += node->val;
        int num = 0;
        num += mp[sum-target]; // return 0 if not exists
        mp[sum] += 1;
        num += traversal(node->left, mp, sum, target);
        num += traversal(node->right, mp, sum, target);
        mp[sum] -= 1;
        return num;
    }
};