[LeetCode] 437. Path Sum III

45 篇文章 0 订阅

原题链接:https://leetcode.com/problems/path-sum-iii/

1. 题目介绍

You are given a binary tree in which each node contains an integer value.
Find 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).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.

给出一个二叉树,每个节点都包含一个整数值。同时给出一个整数 sum
寻找满足以下条件的路径的数目:
1 . 该路径上的所有节点的整数值加起来,和等于sum
2 . 必须是从上向下的路径,只能从父节点到子节点
3 . 是否从根节点开始均可,是否结束于叶子节点均可
给出的树总节点数不超过 1000 个节点,每个节点的整数值范围是从 -1,000,000 到 1,000,000.

Example:
在这里插入图片描述

2. 解题思路

注:以下两种方法参考了 https://blog.csdn.net/zjxxyz123/article/details/80070684 的博文,该博文通俗易懂,介绍全面,有兴趣的读者可以直接去看原文。

方法1 对起点进行分类讨论

解决本问题的难点在于,路径的起点和终点都是不固定的,因此就很难找到一个和为 sum 的路径。

一种解决的方法是,先确定起点,再确定终点。对于任意一棵树来说,起点只有两种情况,第一种情况:起点就是根节点本身,第二种情况:起点位于根节点的子树中。

于是对于每个节点来说,我们都需要计算:
1 . 从它本身开始,满足题目要求的路径个数
2 . 从它左子树中某个节点开始的,满足题目要求的路径个数
3 . 从它右子树中某个节点开始的,满足题目要求的路径个数

将上述3种路径个数加起来,就是以某个节点为根节点的树中,所有满足条件的根节点的路径总数。

实现代码

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int pathSum(TreeNode root, int sum) {
        if(root == null){
            return 0;
        }
        //分为起点就是根节点本身,和起点位于根节点的子树中
        return pathSum(root.left,sum) + pathSum(root.right,sum) + fromRoot(root, sum);
        /*
        fromRoot(root, sum)    是从root本身开始,满足题目要求的路径个数
        pathSum(root.left,sum) 是从root左子树中某个节点开始的,满足题目要求的路径个数
        pathSum(root.right,sum)是从root右子树中某个节点开始的,满足题目要求的路径个数
        */
    }
    
    //求从根节点出发,和为sum的所有路径总个数
    public int fromRoot(TreeNode root, int sum){
        if(root == null){
            return 0 ;
        }
        int res = 0;
        if(root.val == sum){
            res++;
        }
        res += fromRoot(root.left ,sum-root.val);
        res += fromRoot(root.right,sum-root.val);
        return res;
    }
}

方法2 使用HashMap存放段和

这个方法的思路我直接从 https://blog.csdn.net/zjxxyz123/article/details/80070684 摘录而来:

首先有一个引题,那便是给定一个数组,有正有负有0,如何找到子数组和为target的所有子数组个数?

利用HashMap,将从0索引到任一个位置的数组和都加入map中,假设此时数组中已经有了 0~ 0, 0~ 1…0~ i -1 范围的子数组和,那么当遍历到 i 位置时,此时 0~i 的数组和为 curSum,检查map中是否存在curSum - target 值,若有,则说明从数组的某一位置到i位置,这形成的子数组和便是target。

为了不遗漏从0索引开始的位置,初始时,将<0, -1>放入map中。key为路径和,value为索引。
这样便是O(N)下找到了所有子数组和为sum的子数组。

回到该题,如果我们将数中的一条路径看做数组的话,那么便是同样的情况,为了统计路径个个数,key存从根节点到某中间节点的路径和,value存出现该路径和的次数。

可是这是一颗二叉树,存在多条从根到 叶子节点的路径,那么只能通过 DFS + 回溯来控制map,保证map中始终维持的是一条从根到叶子节点的路径相关路径和信息。
时间复杂度:O(N)

具体可以看以下实现代码:

class Solution {
    public int pathSum(TreeNode root, int sum) {
        //记录从根节点到第i个节点的路径和
        //key:根节点到某一节点的路径和,value:该路径的出现次数
        HashMap<Integer, Integer> sumMap = new HashMap<>();
        
        //加这句是为了,防止遗漏从根节点到某一节点的路径和为sum的路径
        sumMap.put(0,1);
        
        return fromRoot(root, 0, sum, sumMap);
    }
    
    //计算以node为根的树中,路径和为sum的所有路径个数
    public int fromRoot(TreeNode node, int preSum, int sum, HashMap<Integer,Integer> sumMap){
        if(node == null){
            return 0 ;
        }
        
        //curSum:从跟节点到当前节点的路径和
        //preSum:从跟节点到当前节点的上一层节点的路径和
        int curSum = preSum + node.val;
        
        int Count = 0;//符合要求的路径个数
        
        //以node节点为终点的、和为sum的路径个数
        Count += sumMap.getOrDefault(curSum-sum , 0);
        
        //将当前路径和统计进sumMap
        sumMap.put(curSum, sumMap.getOrDefault(curSum, 0) + 1);
        
        //继续累加左右子树的结果
        Count += fromRoot(node.left , curSum, sum, sumMap);
        Count += fromRoot(node.right, curSum, sum, sumMap);
        
        //"回溯",保证sumMap里始终位置的是一条路径的某个节点到根节点的和的情况
        sumMap.put(curSum, sumMap.get(curSum) - 1);
        
        return Count;
    }
}

3. 参考资料

https://blog.csdn.net/zjxxyz123/article/details/80070684

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值