「数据结构与算法」动态规划学习笔记:前缀和
前缀和是一种查询数组中任意区间的元素的和的数据结构,这里数组给定之后就不变了。针对这个不变的数组,前缀和用于多次查询区间 [i, j]
上元素的和。
对于动态规划而言,前缀和的意义主要有两点:
- 一维和二维前缀和的推导,分别用到了单串和矩阵中最经典的状态设计以及状态转移
- 在一些更复杂的动态规划问题中,状态转移的时候需要依赖区间和,因为状态转移是非常频繁的操作,因此必须高效地求区间和才能使得状态转移的时间复杂度可接受,此时就必须用到前缀和了
一些问题需要前缀和与其它数据结构配合来解决,也有两类:
- 先预处理出前缀和数组,这一步是动态规划,然后在前缀和数组上用其它数据结构解决
- 还是按照动态规划的方式求前缀和,也需要额外的数据结构维护前缀和,但不是预处理好前缀和数组之后再用数据结构计算,而是每求出一个前缀和,就更新一次数据结构并维护答案
前缀和 简介
给定长度 n
的序列 a (a[0], a[1], ..., a[n-1])
,给每个前缀求一次和, S[0] = 0
, S[i] = a[0] + a[1] + ... + a[i - 1]
。这些前缀和维护在一个长度 n + 1
数组 S
里,称为前缀和数组。
有两类在数组 a 上的求和需求:
- 前缀和:求
a[0..i]
的和 - 区间和:求区间
a[L, R]
的和
对于第 1 种前缀和, S[i + 1]
刚好就是答案,因为这就是前缀和的定义。 S[i+1] = a[0] + a[1] + ... + a[i]
。
对于第 2 种区间和,如果已经有了前缀和数组,两个前缀和 S[R + 1]
和 S[L]
的差刚好就是 [L, R]
上的区间和。已有前缀和数组之后,这一步操作就是 O(1)
的。
定义 sums[k]
为 [0..k-1]
的和,其中 sums[0]
表示数组中没有数字被选中, sums[1]
表示只选中第一个数 nums[0]
。 预先计算 0 ~ k (0<=k<=n-1)
的和,这一系列的和都是从 0
开始的,因此称为前缀和。公式如下:
- k = 0:
sum(0, 0) = 0
- 1 <= k <= n:
sum(0, k) = nums[0] + nums[1] + ... + nums[k - 1]
此后的区间查询都可以利用公式:
sum(i, j) = sums(0, j + 1) - sum(0, i)
线性动态规划中最基础的一种是单串 dp[i]
,并且只与子问题 i - 1
有关,即 dp[i] = f(dp[i-1])
。前缀和就是这种情况, sums[i]
只与 sums[i-1]
有关。推导前缀和数组的过程是 O(N)
的,如果区间和的查询次数达到了 O(N)
那么计算区间和的时间复杂度是摊销 O(1)
的。
数据结构 维护 前缀和
用动态规划的方式推 sums[i]
的时候,在某些题型中计算完 sums[i]
后需要查询以前算过的结果以此来计算某种指标(例如小于、大于、等于某个数值等),此时就需要用数据结构来将前面的计算结果维护起来,以便高效查询。
将前缀和维护在数据结构中,以便于后续的多次查询,最常见的是维护在哈希表 HashMap中。
HashMap 维护 前缀和
Key:前缀和(状态)的值
Value:第一次出现时的索引
经典问题① a[0], a[1], ..., a[n - 1]
上有没有一个区间,其和为 target
关键词:[有没有]、[区间]、[等于]
计算前缀和数组 sums[i]
。当扫描到 i
时, a[0], a[1], ..., a[i - 1]
的前缀和都已经求过了,所以可以在计算的过程中,将前缀和维护在数据结构中,以便于后续的多次查询。本题在之后要查询前缀和的值是否存在,因此维护在HashSet里。
求完当前值 a[i]
对应的前缀和 S[i+1]
, 在插入到HashSet之前先考虑: S[i+1] - target
在HashSet中是否出现过。
- 如果出现:说明存在以
i
结尾的某个区间,和为target
,则找到答案 - 如果不出现:说明不存在以
i
结尾的区间,和为target
,则继续枚举i + 1
经典问题② a[0], a[1], ..., a[n - 1]
上有多少个区间,其和为 target
关键词:[多少个]、[区间]、[等于]
整体解题思路与问题①相似。
区别在于题目要求统计个数,所以需要使用HashMap来替代HashSet并以此维护多个 Key(和的值) - Value(该和的值出现的个数)
键值对。
LeetCode 560 和为 K 的子数组
https://leetcode-cn.com/probl...
经典问题③ a[0], a[1], ..., a[n - 1]
上有多少个区间,其和大于、小于 target
关键词:[多少个]、[区间]、[大于、小于]
LeetCode 327 区间和的个数
https://leetcode-cn.com/probl...
经典问题④ 一棵树上有没有一个路径,其和为 target
关键词:[有没有]、[树]、[等于]
LeetCode 437 路径总和 III
https://leetcode-cn.com/probl...
经典问题⑤ 矩形区域内有多少个子矩阵,其和大于、小于或等于 target
关键词:[有没有]、[多少个]、[矩形区域]、[大于、等于、小于]
LeetCode 363 矩形区域不超过 K 的最大数值和
https://leetcode-cn.com/probl...
LeetCode 1074 元素和为目标值的子矩阵数量
https://leetcode-cn.com/probl...
面试题 17.24 最大子矩阵
https://leetcode-cn.com/probl...
对于矩阵问题,可以优先考虑将二维矩阵压缩成一维数组,然后参考问题①②③的方式使用前缀和进行求解。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。