LeetCode - Medium - 437. Path Sum III

Topic

  • Tree

Description

https://leetcode.com/problems/path-sum-iii/

Given the root of a binary tree and an integer targetSum, return the number of paths where the sum of the values along the path equals targetSum.

The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes).

Example 1:

Input: root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
Output: 3
Explanation: The paths that sum to 8 are shown.

Example 2:

Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: 3

Constraints:

  • The number of nodes in the tree is in the range [0, 1000].
  • − 1 0 9 < = N o d e . v a l < = 1 0 9 -10^9 <= Node.val <= 10^9 109<=Node.val<=109
  • − 1000 < = t a r g e t S u m < = 1000 -1000 <= targetSum <= 1000 1000<=targetSum<=1000

Analysis

方法一:我写的,暴力算法 + DFS。

方法二:别人写的,暴力算法 + DFS,比方法一精简。

方法三:别人写的,利用Map缓存 + DFS,空间换时间。

It’s not very hard if we understand the elementary idea:
Given we have a route:
--------------sum(a,b)-------sum(b,c)
a-------------b--------------c

we know that, sum(a,c) = sum(a,b) + sum(b,c)
if target == sum(b,c), then sum(a,c) = sum(a,b) + target

Now, we could infer that,
if exists a point b, that conforming sum(a,b) = sum(a,c) - target,
the, b->c is the path we want to find. link

Hope my comment below could help some folks better understand this solution.

  1. The prefix stores the sum from the root to the current node in the recursion

  2. The map stores <prefix sum, frequency> pairs before getting to the current node. We can imagine a path from the root to the current node. The sum from any node in the middle of the path to the current node = the difference between the sum from the root to the current node and the prefix sum of the node in the middle.

  3. We are looking for some consecutive nodes that sum up to the given target value, which means the difference discussed in 2. should equal to the target value. In addition, we need to know how many differences are equal to the target value. (More detail , see below quote)

  4. Here comes the map. The map stores the frequency of all possible sum in the path to the current node. If the difference between the current sum and the target value exists in the map, there must exist a node in the middle of the path, such that from this node to the current node, the sum is equal to the target value.

  5. Note that there might be multiple nodes in the middle that satisfy what is discussed in 4. The frequency in the map is used to help with this.

  6. Therefore, in each recursion, the map stores all information we need to calculate the number of ranges that sum up to target. Note that each range starts from a middle node, ended by the current node.

  7. To get the total number of path count, we add up the number of valid paths ended by EACH node in the tree.

  8. Each recursion returns the total count of valid paths in the subtree rooted at the current node. And this sum can be divided into three parts:

    • the total number of valid paths in the subtree rooted at the current node’s left child
    • the total number of valid paths in the subtree rooted at the current node’s right child
    • the number of valid paths ended by the current node

The interesting part of this solution is that the prefix is counted from the top(root) to the bottom(leaves), and the result of total count is calculated from the bottom to the top 😄 link

另外,这单一方法里,将二叉树的前序遍历,后序遍历两模式混合在一起使用,有点回溯算法的味道。

方法四:方法三的另一种写法。

Submission

import java.util.HashMap;

import com.lun.util.BinaryTree.TreeNode;

public class PathSumIII {
	
	//方法一:我写的,暴力算法 + DFS
    public int pathSum(TreeNode root, int targetSum) {
    	int[] findCount = {0};
    	dfs(root, findCount, targetSum);
        return findCount[0];
    }
    
    private void dfs(TreeNode node, int[] findCount, int targetSum) {
    	if(node == null) return;
    	
    	dfs2(node, 0, findCount, targetSum);
    	
    	dfs(node.left, findCount, targetSum);
    	dfs(node.right, findCount, targetSum);
    }
    
    private void dfs2(TreeNode node, int sum, int[] findCount, int targetSum) {
    	if(node == null) return;
    	
    	if((sum += node.val) == targetSum)
    		findCount[0]++;
    	
    	dfs2(node.left, sum, findCount, targetSum);
    	dfs2(node.right, sum, findCount, targetSum);
    }
    
    //方法二:别人写的,暴力算法 + DFS
    public int pathSum2(TreeNode root, int sum) {
        if (root == null) return 0;
        return pathSumFrom(root, sum) + pathSum2(root.left, sum) + pathSum2(root.right, sum);
    }
    
    private int pathSumFrom(TreeNode node, int sum) {
        if (node == null) return 0;
        return (node.val == sum ? 1 : 0) 
            + pathSumFrom(node.left, sum - node.val) + pathSumFrom(node.right, sum - node.val);
    }
    
    
    //方法三:别人写的,利用缓存,空间换时间
    public int pathSum3(TreeNode root, int sum) {
        HashMap<Integer, Integer> preSumMap = new HashMap<>();
        preSumMap.put(0,1);
        int[] count = {0};
        helper(root, 0, sum, preSumMap, count);
        return count[0];
    }
    
    public void helper(TreeNode root, int currSum, int target, HashMap<Integer, Integer> preSumMap, int[] count) {
        if (root == null)
            return;
        
        currSum += root.val;

        if (preSumMap.containsKey(currSum - target)) {
            count[0] += preSumMap.get(currSum - target);
        }
        
        
        if (!preSumMap.containsKey(currSum)) {
            preSumMap.put(currSum, 1);
        } else {
            preSumMap.put(currSum, preSumMap.get(currSum) + 1);
        }
        //上面语块可换写成一句map.put(sum, map.getOrDefault(sum, 0) + 1);
        
        helper(root.left, currSum, target, preSumMap, count);
        helper(root.right, currSum, target, preSumMap, count);
        preSumMap.put(currSum, preSumMap.get(currSum) - 1);
    }
    
    //方法四:方法三的另一种写法
    public int pathSum4(TreeNode root, int sum) {
        if (root == null) return 0;
        HashMap<Integer, Integer> map = new HashMap<>();
        map.put(0, 1);
        return findPathSum(root, 0, sum, map);  
    }
    
    private int findPathSum(TreeNode curr, int sum, int target, HashMap<Integer, Integer> map) {
        if (curr == null) {
            return 0;
        }
        // update the prefix sum by adding the current val
        sum += curr.val;
        
        // get the number of valid path, ended by the current node
        int numPathToCurr = map.getOrDefault(sum - target, 0); 
        
        // update the map with the current sum, so the map is good to be passed to the next recursion
        map.put(sum, map.getOrDefault(sum, 0) + 1);
        
        // add the 3 parts discussed in 8. together
        int res = numPathToCurr + findPathSum(curr.left, sum, target, map)
                                               + findPathSum(curr.right, sum, target, map);
        // restore the map, as the recursion goes from the bottom to the top
        map.put(sum, map.get(sum) - 1);
        return res;
    }

}

Test

import static org.junit.Assert.*;
import org.junit.Test;

import com.lun.util.BinaryTree;
import com.lun.util.BinaryTree.TreeNode;

public class PathSumIIITest {

	@Test
	public void test() {
		PathSumIII obj = new PathSumIII();

		TreeNode root1 = BinaryTree.integers2BinaryTree(10, 5, -3, 3, 2, null, 11, 3, -2, null, 1);
		assertEquals(3, obj.pathSum(root1, 8));
		assertEquals(3, obj.pathSum2(root1, 8));
		assertEquals(3, obj.pathSum3(root1, 8));
		assertEquals(3, obj.pathSum4(root1, 8));
				
		TreeNode root2 = BinaryTree.integers2BinaryTree(5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1);
		assertEquals(3, obj.pathSum(root2, 22));
		assertEquals(3, obj.pathSum2(root2, 22));
		assertEquals(3, obj.pathSum3(root2, 22));
		assertEquals(3, obj.pathSum4(root2, 22));
	}
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值