前缀和算法与LeetCode437

啥是前缀和

对于一个给定的数列 A, 它的前缀和数列S 是通过递推能求出来得:
S [ i ] = ∑ j = 1 i A [ j ] S[i]=\sum_{j=1}^{i}A[j] S[i]=j=1iA[j]

部分和是指数列A中某个下标区间的数之和,可理解为前缀和的相减:
s u m [ l , r ] = ∑ i = l r A [ i ] = S [ r ] − S [ l − 1 ] sum[l, r]=\sum_{i=l}^{r}A[i]=S[r]-S[l-1] sum[l,r]=i=lrA[i]=S[r]S[l1]
在路径类问题中,前缀和可定义为到当前节点路径上的元素之和。如果两个节点的前缀和相等,则说明节点间的距离为0;如果前缀和相差为target,说明节点间存在距离target的路径

例题1

这里以LeetCode437为例,传统二重DFS的解法时间复杂度是O(n^2)。如果使用前缀和建模,本题中的路径是一棵树,从根往任一节点的路径上(不走回头路),有且仅有一条路径

简单情形

先分析个简单例子:如下图,所有节点没有左子树,因此变成单一路径。我们初始化一个<K, V>结构来记录前缀和的出现与否,此时前缀和的存在等同于节点的存在。已知某个节点的前缀和,若要看与它相距sum的节点存在与否,直接preSum_Cur + sum或者preSum_Cur - sum并检查其结果是否存在。

在这里插入图片描述

注意在初始化时,前缀和0也要算进去。为了节省遍历次数,只判断每个节点减去距离后的节点是否存在,即寻找已走过的节点是否存在。如下图,前缀和3的节点减去距离3后变为0,而0已经存在;前缀和6的节点减去距离3后变为3,3也已经存在,所以总共2条路经。

在这里插入图片描述

复杂情形

再看看更加普适的情形,下图中的二叉树结构,仔细思考就可发现:在遍历的过程中,在递归的每个阶段仍然可看作是单条路径。

在这里插入图片描述

因此,在这个阶段可以用简单情形的判断方式对结果计数,但要注意每层递归结束时,要将<K,V>中的出现状态置为否,这样在进入另一条分支(比如,刚从左子树递归回来,正要进入右子树),这样才不会产生干扰,从而获得正确结果。如上图,11、1、3节点减去距离后的前缀和均是10(根节点),因此存在3条路径。

注意事项

因为我们map的初始值是<0,1>,这就造成特殊情况下的统计出错——根节点的值恰好是0的情况,正常状态下,递归进入根节点(假设它非零)后,map中应该是<0, 1>;<roo.val, 1>,但当根节点为零时造成了两条记录的重合。为了解决这一特殊情况,可对出现次数加一处理,具体代码参照一下版本:

public void helper(TreeNode root, Map<Integer, Integer> map, int cur, int sum){
    if (root == null)
        return;
    cur += root.val; // 前缀和
    if( map.getOrDefault(cur-sum, 0)>0 )
        res+=map.getOrDefault(cur-sum, 0);
    map.put(cur, map.getOrDefault(cur, 0)+1);	// 出现,则次数加一
    helper(root.left, map, cur, sum);
    helper(root.right, map, cur, sum);
    map.put(cur, map.getOrDefault(cur, 0)-1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值