zoukankan      html  css  js  c++  java
  • Leetcode437 路径总和 III 双递归与前缀和

      最简单的思路为双递归,内部递归函数用于计算以 node 节点为头元素的路径和, 外部递归函数用于遍历所有节点。

      即遍历以每个节点为头元素的所有符合条件的路径:

        /**
         * @Author Niuxy
         * @Date 2020/7/15 11:04 下午
         * @Description 双递归
         */
        public int pathSum(TreeNode root, int sum) {
            if (root == null) {
                return 0;
            }
            return pathSum0(root, sum, 0) + pathSum(root.right, sum) + pathSum(root.left, sum);
        }
    
        public int pathSum0(TreeNode node, int sum, int preSum) {
            if (node == null) {
                return 0;
            }
            int currentSum = preSum + node.val;
            int re = pathSum0(node.left, sum, currentSum) + pathSum0(node.right, sum, currentSum);
            if (sum == currentSum) {
                re++;
            }
            return re;
        }

      计算过程存在大量重复求和计算。比如 [1,2,3,4,5] ,1-5 的路径和为 2-5 的路径和加 1 ,而在计算过程中,2-5 的路径和被计算了两次。

      内部函数建立缓存只能由 node,currentSum 两个坐标为依据,但这两个坐标命中的结果,整个计算过程只被使用过一次。也就是说内部函数的定义无法帮我们找出上述的重复计算情况。

      参考数组求区间和时,可以使用双指针复用局部的区间和。

      也可以在遍历一条路径时,通过区间和的差来复用该路径上的区间和。

      从头元素到节点 i 的区间和为 I ,到节点 j 的区间和为 J 。则节点 i 到 j 的区间和为 J-I 。其中 I 与 J 即为节点 I 与 节点 J 的前缀和。

      在遍历一条路径时,记录下每一个节点的前缀和,通过前缀和的差值寻找区间和为某值的路径是否存在。

      因为要寻找路径的总和是确定的 sum ,某节点的前缀和 currentSum 是确定的。寻找的区间为 currentSum-x = sum , 也就是遍历每个节点时,寻找其前面前缀和为 x = currentSum-sum

    的节点的数量。因为 x 是确定的,在一条路径上嗅探时,可以用哈希表存储前面节点的前缀和,以 O(1) 时间复杂度寻找前缀和为 x 的节点。

      语义为遍历每个节点时,寻找该路径上,所有以该节点为尾结点的符合题意的区间。

      目标区间的尾结点必为该路径上某一节点,该方式可完整遍历解空间,并帮助我们避免重复的求和计算。

    /**
         * @Author Niuxy
         * @Date 2020/7/15 11:52 下午
         * @Description 前缀和解法
         */
        public final int pathSum1(TreeNode root, int sum) {
            return pathSum1(root, sum, 0, new HashMap<Integer, Integer>());
        }
    
        public final int pathSum1(TreeNode root, int sum, int preSum, Map<Integer, Integer> cache) {
            if (root == null) {
                return 0;
            }
            int currentRe = 0;
            int currentSum = preSum + root.val;
            if (currentSum == sum) {
                currentRe++;
            }
            //以该节点为末尾节点,向上寻找满足条件的路径数: currentRe - x = sum -> x = currentSum - sum
            currentRe += cache.getOrDefault(currentSum - sum, 0);
            //本节点前缀和
            cache.put(currentSum, cache.getOrDefault(currentSum, 0) + 1);
            currentRe += pathSum1(root.left, sum, currentSum, cache) + pathSum1(root.right, sum, currentSum, cache);
            cache.put(currentSum, cache.get(currentSum) - 1);
            return currentRe;
        }

      与数组双指针遍历区间和的思路类似,前缀和也是避免重复计算的一种思路。

      同时,在“以某个节点为头节点”进行遍历时无法避免的重复计算,转变为“以某个元素为尾结点”进行遍历便解决了。

      因为“以某个节点为头节点”时,已计算的结果不足以支撑计算出当前结果。“以某个元素为尾结点”则是在根节点与尾结点间寻找子路径,以计算出的前缀和结果足以支撑计算出当前结果,以此可以避免重复计算。

      在很多情况下,逆向思维是破局的不二法门。但局内逻辑繁多,确定可以在哪个逻辑上尝试逆向,需要大量的练习。

  • 相关阅读:
    轻量级分布式任务调度框架(二、LTS编译、打包、部署)
    轻量级分布式任务调度框架(一、LTS简介、特点、工作流程)
    MySQL数据库学习一
    Navicat 连接 SQL Server 数据库,报错 08001
    noVNC 遇到一个错误: Uncaught TypeError: Cannot read property 'forEach' of undefined
    加强自己VPS服务器安全的一次经历
    Python 编码错误的本质和解决方案
    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'data' at line 1
    requests爬虫请求报错:UnicodeEncodeError: 'latin-1' codec can't encode character 'u2026' in position 30
    docker无法删除镜像,Error: No such container,附docker常用命令
  • 原文地址:https://www.cnblogs.com/niuyourou/p/13311376.html
Copyright © 2011-2022 走看看