原题链接: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;
}
}