LeetCode 437. Path Sum III

问题描述

  • 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.
  • Example :
    这里写图片描述
  • 地址

问题分析

  • 以前都是要求 root to leaf ,而该题是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).
  • 解法1 :
    • 一颗以root为根的树,其路径和等于sum的路径个数可由以下三部分组成:
      • 路径以root 开头
      • 路径不以root开头
        • 左子树中等于sum的路径个数
        • 右子树中等于sum的路径个数
    • 所以以上是两个递归,而递归的终止条件又有所不同,递归终止条件为空结点结束
    • 时间复杂度: T(N) = 2T(N/2) + O(N),根据Master公式,可知 复杂度为O(NlogN)
  • 解法2 :
    • 该解法真是将DFS吃透了。
    • 首先有一个引题,那便是给定一个数组,有正有负有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)

经验教训

  • 两种思路都很巧妙,尤其第二种,简直巧夺天工。多看多理解 dfs+ 回溯

代码实现

  • 方法1:
    //pathSum: 返回以root为根的树中,所有路径和为sum的路径个数(以root开头,不以root开头)
    public int pathSum(TreeNode root, int sum) {
        if (root == null) {
            return 0;
        }
        //由三部分组成:不以root开头的路径(1. 递归左子树结果 2.递归右子树结果 )
        //3. 必须以root开头的路径
        return pathSum(root.left, sum) + pathSum(root.right, sum) + findPath(root, sum);
    }

    //统计必须从root出发,路径和为sum的所有路径个数
    public int findPath(TreeNode root, int sum) {
        if (root == null) {
            return 0;
        }
        int res = 0;//由三部分组成
        if (sum == root.val) { //已经找到了以root结尾的路径。累加进res
            res += 1;
        }
        //因为后序路径可能存在类似于加n减n的情况,所以需要继续递归寻找左右子树
        res += findPath(root.left, sum - root.val);
        res += findPath(root.right, sum - root.val);
        return res;
    }
  • 解法2:
    public int pathSum(TreeNode root, int sum) {
        HashMap<Integer, Integer> sumMap = new HashMap<>();
        //加这句是为了,防止遗漏从根节点到某一节点的路径和为sum的路径
        sumMap.put(0,1);
        return dfs(root, 0, sum, sumMap);
    }

    //dfs:得到以root为根的树中,路径和为sum的所有路径个数
    public int dfs(TreeNode root, int preSum, int sum, HashMap<Integer, Integer> sumMap) {
        if (root == null) {
            return 0;
        }
        int curSum = preSum + root.val;
        int res = 0;
        //统计所有以root为结尾,并且路径和为sum的路径个数
        res += sumMap.getOrDefault(curSum - sum, 0);
        //将当前路径和统计进sumMap
        sumMap.put(curSum, sumMap.getOrDefault(curSum, 0) + 1);
        //继续累加左右子树的结果
        res += dfs(root.left, curSum, sum, sumMap) + dfs(root.right, curSum, sum, sumMap);
        //"回溯",保证sumMap里始终位置的是一条路径的某个节点到根节点的和的情况
        sumMap.put(curSum, sumMap.get(curSum) - 1);
        return res;
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值