B站视频:https://www.bilibili.com/video/BV1r4411k7dL?spm_id_from=333.337.search-card.all.click
前缀和
前缀和是一种重要的预处理,能大大降低查询的时间复杂度。可以简单理解为“数列的前n项的和”。根据数组的维度,前缀和有一维前缀和和二维/多维前缀和。
一维前缀和
一维前缀和就是一维数组前n项的和。求解一维前缀和递推公式如下:
s
u
m
[
i
]
=
{
a
[
0
]
,
i=0
s
u
m
[
i
−
1
]
+
a
[
i
]
,
i>0
sum[i] = \begin{cases} a[0], & \text{i=0} \\ sum[i-1]+a[i], & \text{i>0} \end{cases}
sum[i]={a[0],sum[i−1]+a[i],i=0i>0
当我们求出前缀和数组后,我们可以使用O(1)的时间复杂度求出区间[q1, q2]
的区间和:
interval[q1, q2] = sum[q2] - sum[q1 - 1]
求解前缀和数组的java代码如下:
int[] sum = new int[nums.length];
sum[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
sum[i] = sum[i - 1] + nums[i];
}
有的地方会设置前缀和数组第0项sum[0]=0,递归公式为
sum[i]=sum[i-1]+a[i-1]
:int[] sum2 = new int[nums.length + 1]; for (int i = 0; i < nums.length; i++) { sum2[i + 1] = sum[i] + nums[i]; }
LeetCode练习题
二维前缀和
二维前缀和就是二维数组的前缀和,sum[i][j]
为上顶点(0, 0)到下顶点(i, j)组成矩阵的各项和:
二维前缀和的递推公式如下:
s
u
m
[
i
]
[
j
]
=
{
a
[
0
]
[
0
]
,
i=0, j=0
s
u
m
[
0
]
[
j
−
1
]
+
a
[
0
]
[
j
]
,
i=0, j>0
s
u
m
[
i
−
1
]
[
0
]
+
a
[
i
]
[
0
]
,
i>0, j=0
s
u
m
[
i
−
1
]
[
j
]
+
s
u
m
[
i
]
[
j
−
1
]
−
s
u
m
[
i
−
1
]
[
j
−
1
]
+
a
[
i
]
[
j
]
,
i>0, j>0
sum[i][j] = \begin{cases} a[0][0], & \text{i=0, j=0} \\ sum[0][j-1]+a[0][j], & \text{i=0, j>0} \\ sum[i-1][0]+a[i][0], & \text{i>0, j=0} \\ sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j], & \text{i>0, j>0} \end{cases}
sum[i][j]=⎩⎪⎪⎪⎨⎪⎪⎪⎧a[0][0],sum[0][j−1]+a[0][j],sum[i−1][0]+a[i][0],sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1]+a[i][j],i=0, j=0i=0, j>0i>0, j=0i>0, j>0
图中红色区间sum[i-1][j-1]
为重复加的部分,因此需要减去。
当我们求出二维数组的前缀和后,同样可以使用O(1)的时间复杂度求出区间[(q1, q2), (q3, q4)]的各项和:
interval[(q1, q2), (q3, q4)] = sum[q3][q4] - sum[q1 - 1][q4] - sum[q3][q2 - 1] + sum[q1 - 1][q2 - 1]
LeetCode练习
差分
差分和前缀和可以说是一对“好基友”,差分是前缀和的逆运算,如果存在原数组a[i],那么对差分数组求前缀和就是原数组:
d
[
i
]
=
{
a
[
0
]
,
i=0
a
[
i
]
−
a
[
i
−
1
]
,
i>0
d[i] = \begin{cases} a[0], & \text{i=0} \\ a[i]-a[i-1], & \text{i>0} \end{cases}
d[i]={a[0],a[i]−a[i−1],i=0i>0
一维差分一般用于这样的场景:多次对序列的某个区间加上一个数,并在最后询问序列是什么样的。如LeetCode1094题 拼车。这类题目一般会先对序列求出差分数组,如果题目要求原数组区间[q1, q2)+ 2,那么差分数组只需要操作下面两步:
d[q1]=d[q1]+2;
d[q2]=d[q2]-2;
操作完成后对差分数组求前缀和就能把原数组还原回来。