欢迎来到我的博客
一维前缀和
基本原理
前缀和算法是一种通过预处理数组元素,计算从数组起始位置到当前位置的累积和的方法。这样,我们可以在O(1)时间内获取任意区间的和。
给出一个数组int nums[5] = {1, 2, 3, 4, 5}
它的前缀和为int presum[5] = {1, 3, 6, 10, 15}
即{ 1, 1 + 2, 1 + 2 + 3, 1 + 2 + 3 + 4, 1 + 2 + 3 + 4 + 5}
int main() {
int nums[5] = { 1, 2, 3, 4, 5 };
int presum[5] = { 1, 3, 6, 10, 15 };
presum[0] = nums[0];
for (int i = 1; i < 5; i++) {
presum[i] = nums[i] + presum[i - 1];
}
}
用途
1.求数组前n个数之和
求数组前三个数之和:presum[2]
求数组所有数之和:presum[n - 1]
2.求数组区间和
[L,R]
---->presum[R] - presum[L - 1]
presum[L - 1]
指 nums
数组中的nums[L - 1]
前面所有数字之和(包括它自己)
preSum[R]
表示的是 nums
数组中num[R]
前面所有数字之和(包括它自己)
例如我们想求解nums
数组第2
到4
个数的区间和,也就是nums
数组对应nums[1]
到nums[3]
的区间,我们只需使用presum[3] - presum[1 - 1]
即可得到
int nums[5] = { 1, 2, 3, 4, 5 };
int presum[5] = { 1, 3, 6, 10, 15 };
presum[3] - presum[0] = (1 + 2 + 3 + 4)-(1)
相减得到2 + 3+ 4
给你一个由
n
个元素组成的整数数组nums
和一个整数k
。请你找出平均数最大且 长度为
k
的连续子数组,并输出该最大平均数。任何误差小于
10-5
的答案都将被视为正确答案。
double findMaxAverage(int* nums, int numsSize, int k) {
// 计算前缀和
int* presum = (int*)malloc(numsSize * sizeof(int));
presum[0] = nums[0];
for (int i = 1; i < numsSize; ++i) {
presum[i] = presum[i - 1] + nums[i];
}
// 初始化最大值为前 k 个元素的和
int max = presum[k-1];
// 使用前缀和计算子数组的和
for (int i = 0; i < numsSize - k; ++i) {
max = fmax(presum[i + k] - presum[i], max);
}
free(presum);
return (double)max / k;
}
二维前缀和
基本原理
二维前缀和实际上是一个新的二维数组,其中每个元素表示原始矩阵中左上角元素到当前元素的所有元素的和。
1 2 3 1 3 6
4 5 6 5 12 21
7 8 9 12 27 45
二维前缀和数组公式:
prefixSum[i][j]
=matrix[i][j]
+prefixSum[i−1][j]
+prefixSum[i][j−1]
-prefixSum[i−1][j−1]
prefixSum[i−1][j]
:上方元素 (i-1, j)
在 prefixSum
中的值。
prefixSum[i][j−1]
:左方元素 (i, j-1)
在 prefixSum
中的值.
prefixSum[i−1][j−1]
:左上方元素 (i-1, j-1)
在 prefixSum
中的值。
边界问题,需要特判
当(i=0,j=0)
prefixSum[0][0] = matrix[0][0]
当i = 0
prefixSum[0][j] = prefixSum[0][j-1] + matrix[0][j]
当j = 0
prefixSum[i][0] = prefixSum[i-1][0] + matrix[i][0]
用途
求二维数组区间和
给出公式
sum[x1,y1][x2,y2]
= sum[x2][y2]
- sum[x2][y1-1]
- sum[x1-1][y2]
+ sum[x1-1][y1-1]
当
(i=0,j=0)
Sum[0,0][0,0] = matrix[0,0]
当x = 0
Sum[0,y1][0,y2] = Sum[0][y2] - Sum[0][y1-1]
当y=0
Sum[x1,0][x2,0] = Sum[x1][0] - Sum[x2-1][0]
例题
计算【1,1】【2,3】之间数字和
1 2 3 4
5 6 7 8
9 10 11 12
计算出前缀和数组
1 3 6 10
6 14 24 36
15 33 54 78
sum[2][3] - sum[2][0] - sum[0][3] + sum[0,0]
78-15-10+1 = 54
给出以下代码供参考
#include <stdio.h>
int calculateRegionSum(int prefixSum[4][5], int row_start, int row_end, int col_start, int col_end) {
return prefixSum[row_end + 1][col_end + 1] - prefixSum[row_start][col_end + 1]
- prefixSum[row_end + 1][col_start] + prefixSum[row_start][col_start];
}
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 创建一个数组存储前缀和
int prefixSum[4][5] = { 0 };
// 计算前缀和数组
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= 4; j++) {
prefixSum[i][j] = matrix[i - 1][j - 1] + prefixSum[i - 1][j]
+ prefixSum[i][j - 1] - prefixSum[i - 1][j - 1];
}
}
int sum = calculateRegionSum(prefixSum, 1, 2, 1, 3);
printf("%d\n", sum);
return 0;
}
一维差分
差分算法的核心思想是将原始数组转化为差分数组,
其中差分数组的每个元素表示原始数组中相邻元素之差
这种转换可以使得在原数组的某个区间内进行批量更新变得非常高效。
基本原理
差分数组 diff
通过以下方式计算:
for (int i = 1; i < n; ++i) {
diff[i] = nums[i] - nums[i-1];
}
diff[i]
就表示原数组 nums[i]
和 nums[i-1]
之差
例如
nums[5] = { 1, 5, 8, 9, 6 };
diff[5] = { 1, 4, 3, 1, -3 };
pre[5] = {1, 5 ,8 ,9 ,6}
差分数组的前缀和就是原数组
用途
1.区间增量
给定数组
nums
,需要在某个区间[start, end]
内的所有元素增加一个常量值val
。
diff[start] += val;
diff[end + 1] -= val;
差分数组中还原成原数组的时候
start
后面的数都会累加,然后再end+1
位在减去这个数停止累加
diff[start] += val;
使得presum[start]
后面都加上了val
,
diff[end + 1] -= val;
同理使presum[end + 1]
后面都减去val
,
相当于
presum[start]~presum[end]
+val
presum[end+1]~presum[lenth-1]
+val
-val
相当于
在区间 [start, end]
内的所有元素增加一个常量值 val
。
接下来我们只需遍历一次差分数组根据前缀和计算就得到更新后的数组。
例如
nums[5] = { 1, 5, 8, 9, 6 }
diff[5] = { 1, 4, 3, 1, -3 }
对以下两个区间内数加val
[1, 3] + 2
------- diff[1]+=2
diff[4]-=2
------- diff[5] = { 1, 6, 3, 1, -5 }
[2, 4] + 3
------- diff[2]+=3
------- diff[5]-=3
diff[5] = {1, 6, 6, 1, -5 }
diff[5] = {1, 6, 6, 1, -1 }
pre[5] = {1, 7, 13, 14, 9}
这里给出上述例子代码供参考
#include <stdio.h>
void DiffArray(int diff[], int start, int end, int val) {
diff[start] += val;
if (end + 1 < 5) {
diff[end + 1] -= val;
}
}
int main() {
int nums[] = { 1, 5, 8, 9, 6 };
int diff[] = { 1, 4, 3, 1, -3 };
int presum[5];
DiffArray(diff, 1, 3, 2);// 区间 [1, 3] 加 2
DiffArray(diff, 2, 4, 3);// 区间 [2, 4] 加 3
presum[0] = diff[0];
for (int i = 1; i < 5; ++i) {
presum[i] = presum[i - 1] + diff[i];
}
for (int i = 0; i < 5; ++i) {
printf("%d ", presum[i]);
}
return 0;
}
二维差分
基本原理
d[x1][y1] += val
d[x2+1][y1] -= val
d[x1][y2+1] -= val
d[x2+1][y2+1] += val
用途
对以下数组做如下处理
[0,0][2,1]+3
[1,1][2,2]-1
1 2 3 4
5 6 7 8
9 10 11 12
先差分标记[0,0][2,1]+3
3 0 -3 0 0
0 0 0 0 0
0 0 0 0 0
-3 0 3 0 0
再标记 [1,1][2,2]-1
3 0 -3 0 0
0 -1 0 1 0
0 0 0 0 0
-3 1 3 -1 0
计算前缀和
3 3 0 0 0
3 2 -1 0 0
3 2 -1 0 0
0 0 0 0 0
将该数组对应加到原数组
4 5 3 4
8 8 6 8
12 12 10 12
得到答案
给出如下代码供参考:
#include <stdio.h>
void add(int d[4][5],int matrix[3][4], int x1, int y1, int x2, int y2, int val) {
d[x1][y1] += val;
d[x2 + 1][y1] -= val;
d[x1][y2 + 1] -= val;
d[x2 + 1][y2 + 1] += val;
}
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int d[4][5] = { 0 };
add(d,matrix, 0, 0, 2, 1, 3);
add(d,matrix, 1, 1, 2, 2, -1);
int prefixSum[4][5] = { 0 };
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 5; j++) {
if (i == 0 && j == 0) {
prefixSum[0][0] = d[0][0];
} else if (i == 0 && j!=0){
prefixSum[0][j] = prefixSum[0][j - 1] + d[0][j];
} else if (i != 0 && j == 0) {
prefixSum[i][0] = prefixSum[i - 1][0] + d[i][0];
}
else {
prefixSum[i][j] = d[i][j] + prefixSum[i - 1][j]
+ prefixSum[i][j - 1] - prefixSum[i - 1][j - 1];
}
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
matrix[i][j] += prefixSum[i][j];
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
感谢您的阅读