前缀和算法详解及 LeetCode 经典题目解析
注意:前缀和表示以 nums[0] 为起始位置,一直到遍历位置所有元素组成的数组之和。
1. 寻找数组的中心下标
题目链接: LeetCode - Find Pivot Index
题目解析

算法原理
解法一:暴力解法
在遍历到一个元素时,判断这个元素左右的元素之和是否相等即可。每次遍历到一个元素,就要左边枚举一遍,右边枚举一遍;枚举一个元素的左右之和,时间复杂度为 O(N),所以暴力解法 O(N^2)。
解法二:使用前缀和数组
我们在求 [0, i - 1] 和 [i+1 , n - 1] 区间的元素之和,就是在求一段连续区间的和,这就可以利用前缀和的预处理。
预处理前缀和数组和后缀和数组
因为本题需要求前面区间的和与后面区间的和,所以我们可以调整一下前缀和数组的表达方式。
我们用 f 表示前缀和数组,g 表示后缀和数组:

f[i] 表示 [0, i-1] 区间的元素之和,而前面的 dp[i] 表示的是 [1, i] 区间的元素之和,这题这样微调,是因为数组从 0 下标开始,且前缀和数组的元素 f[i],是不包括 arr[i] 元素的求和值; g[i] 表示 [i+1, n-1] 区间所有元素之和,之前是求 [i, n] 区间的和,微调的原理同上;
递推公式:

使用前缀和数组和后缀和数组
我们可以在 [0, n - 1] 区间,直接枚举预处理好的前缀和与后缀和数组,并且判断当前枚举的预处理数组元素 f[i], g[i] 是否相等,相等返回最左边的中心下标,否则返回 -1。

处理细节问题
- 初始化问题
- f(0):按照如上的方法去填 f[0] 时,会出现越界访问。我们可以提前把 f(0) 的值算一下,f(0) 表示的是负无穷到 -1 的区域,也就没有元素,所以 f[0]=0。
- g(n-1):当 i = n-1 时,也会出现越界访问。所以 g[n-1] 同理也初始化为 0。



在计算 nums[i] 时=前,我们可以预处理前缀积数组&后缀积数组;当计算 nums[i] 时,我们就直接从预处理的数组拿值,就可以把计算 answer[i] 的时间复杂度降到 O(N)。


而在填 answer 时,只需要计算 answer[i] = f[i-1] * g[i+1]。

当枚举 i 位置时,其实我们已经知道以 i 位置为结尾的前缀和数组对应 i 位置的元素 sum[i] 是多少了。
所以,我们只需要找到一段区间的前缀和,让这个区间的前缀和等于 sum[i] - k 即可。
所以问题就转化成:在 [0, i-1] 区间内,有多少个前缀和为 sum[i] - k 的子数组。
而 sum % k = x % k,也可以反过来推出绿色部分元素之和 % k = 0。
所以,问题就转换成:求在 [0, i - 1] 区间内,找到有多少给前缀和 x 通过与 k 取余,x%k=sum%k,进而就能反推出绿色部分的子数组。

我们演示示例一求 answer 矩阵的一个元素 answer[0][0]:
并且,如果扩展的矩阵超出原始边界,超出的元素格子都不考虑:



多加一行一列之和,我们是先根据 mat 来填 dp 表,再通过 dp 表填 answer,所以,在给 dp 表加上一行一列后,就会出现 mat 和 dp 的下标映射关系问题:
我们在求 dp[i][j] 时,在调整后,就变成找 mat[i-1][j-1] 对应的矩阵和,因此,我们的递推公式也会发送改变:


