【入门级-算法-4、算法策略:前缀和】
1、前缀和的概念
是一种预处理数据的算法策略,核心是提前计算数组中“从起始位置到每个位置的元素和”,大幅提升效率。
2、核心思想
前缀和是一种预处理技术,是预先计算并存储数组从起始位置到每个位置的元素累加和,从而将区间求和的复杂度从 O(n) 降为 O(1)。它的本质是用 “空间换时间”,通过一次预处理存储中间结果,避免后续重复计算,特别适合需要频繁查询数组区间和的场景。
3、基本定义
一个已经存在的数组 nums[0…n-1],我们定义它的前缀和数组 为: prefix[0…n]
数组prefix[0…n]来保存数组 nums[0…n-1]中元素的和
prefix[0] = 0 (或者 prefix[0] = nums[0],根据约定不同)
prefix[i] = nums[0] + nums[1] + … + nums[i-1]
更常见的定义(包含空集,便于计算):
prefix[0] = 0
prefix[1] = nums[0]
prefix[2] = nums[0] + nums[1]
…
prefix[i] = nums[0] + nums[1] + … + nums[i-1]
4、为什么需要前缀和?
假设有一个数组,我们需要频繁地查询某个区间 [L, R] 内元素的和。
传统方法:每次查询都遍历区间 [L, R] 进行累加。
时间复杂度:O(n) 每次查询
如果有 m 次查询,总复杂度:O(m × n),当 m 和 n 很大时效率极低。
前缀和方法:
预处理:花费 O(n) 时间构建前缀和数组。
每次查询:只需要 O(1) 时间计算。
m 次查询总复杂度:O(n + m),效率极大提升。
应用场景总结
频繁区间求和:统计任意区间的和、平均值等
子数组问题:寻找和为k的子数组、最大子数组和等
二维区域计算:图像处理、矩阵统计
配合哈希表:解决更复杂的子数组问题
实时数据分析:需要快速响应区间查询的系统
5、举例说明
假设有数组:nums = [2, 5, 1, 3, 4]
索引: 0 1 2 3 4
nums: [2, 5, 1, 3, 4]
构建前缀和数组:prefix[6]
prefix[0] = 0
prefix[1] = 0 + 2 = 2
prefix[2] = 2 + 5 = 7
prefix[3] = 7 + 1 = 8
prefix[4] = 8 + 3 = 11
prefix[5] = 11 + 4 = 15
前缀和数组: prefix = [0, 2, 7, 8, 11, 15]
代码实现
#include <stdio.h>
#include <stdlib.h>
// 构建前缀和数组
void build_prefix_sum(int nums[], int n, long long prefix[]) {
prefix[0] = 0;
for (int i = 1; i <= n; i++) {
prefix[i] = prefix[i - 1] + nums[i - 1];
}
}
// 查询区间和 [L, R](0-indexed)
long long query_range_sum(int L, int R, long long prefix[]) {
return prefix[R + 1] - prefix[L];
}
// 打印数组
void print_array(char* name, long long arr[], int size) {
printf(“%s: [”, name);
for (int i = 0; i < size; i++) {
printf(“%lld”, arr[i]);
if (i < size - 1) printf(“, “);
}
printf(”]\n”);
}
int main() {
int nums[] = {2, 5, 1, 3, 4};
int n = sizeof(nums) / sizeof(nums[0]);
// 前缀和数组(长度比原数组多1)
long long* prefix = (long long*)malloc((n + 1) * sizeof(long long));
// 构建前缀和
build_prefix_sum(nums, n, prefix);
printf("原始数组: ");
for (int i = 0; i < n; i++) {printf("%d ", nums[i]);
}
printf("\n");
print_array("前缀和数组", prefix, n + 1);
// 测试查询
printf("\n区间和查询:\n");
printf("sum(1, 3) = %lld\n", query_range_sum(1, 3, prefix)); // 5+1+3=9
printf("sum(0, 4) = %lld\n", query_range_sum(0, 4, prefix)); // 2+5+1+3+4=15
printf("sum(2, 2) = %lld\n", query_range_sum(2, 2, prefix)); // 1
free(prefix);
return 0;
}