问题描述
今天在牛客上刷算法题时,碰到这样一道题,题目要求我们用前缀和解题,但是我并没有太深入了解过这个算法,于是只能暴力求解,但是发现提交后都显示超时或有些案例不通过,我百思不得其解,于是通过网上的搜索和看题解的方法终于对前缀和问题有了清晰的认识。
题目如下:
解法对比
方法一:暴力解法
最直观的想法就是每次查询都遍历区间 [l, r],累加所有元素。
import java.util.Scanner;
public class ForceDemo {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int q = sc.nextInt();
int[] arr = new int[n + 1];
for (int i = 1; i <= n; i++) {
arr[i] = sc.nextInt();
}
for (int i = 0; i < q; i++) {
int l = sc.nextInt();
int r = sc.nextInt();
// 暴力计算区间和
long sum = 0;
for (int j = l; j <= r; j++) {
sum += arr[j];
}
System.out.println(sum);
}
sc.close();
}
}
时间复杂度: O(m × n),最坏情况下每次查询都要遍历整个数组
空间复杂度: O(n)
方法二:前缀和优化
前缀和的核心思想是预处理,用空间换时间。我们先计算出前缀和数组,然后利用公式快速计算区间和。
关键公式: sum[l, r] = prefixSum[r] - prefixSum[l-1]
import java.util.Scanner;
public class PrefixSum {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int q = sc.nextInt();
int[] arr = new int[n + 1];
long[] prefixSum = new long[n + 1];
// 读入数组并构建前缀和
for (int i = 1; i <= n; i++) {
arr[i] = sc.nextInt();
prefixSum[i] = prefixSum[i - 1] + arr[i];
}
// 处理查询
for (int i = 0; i < q; i++) {
int l = sc.nextInt();
int r = sc.nextInt();
// O(1)计算区间和
long sum = prefixSum[r] - prefixSum[l - 1];
System.out.println(sum);
}
sc.close();
}
}
时间复杂度: O(n + m),预处理O(n),每次查询O(1)
空间复杂度: O(n)
算法原理详解
前缀和的构建过程
假设原数组为:[3, 5, 2, 7, 1]
前缀和数组的构建:
prefixSum[0] = 0
prefixSum[1] = 0 + 3 = 3
prefixSum[2] = 3 + 5 = 8
prefixSum[3] = 8 + 2 = 10
prefixSum[4] = 10 + 7 = 17
prefixSum[5] = 17 + 1 = 18
区间和计算
要计算区间 [2, 4] 的和:
sum[2,4] = prefixSum[4] - prefixSum[1] = 17 - 3 = 14
prefixSum[4] 表示前4个元素的和:3+5+2+7= 17,而prefixSum[1] 表示前1个元素的和:3
最后两者相减得到第2到第4个元素的和为14。
性能分析
解法 | 预处理时间 | 单次查询时间 | 总时间复杂度 |
---|---|---|---|
暴力解法 | O(1) | O(n) | O(m × n) |
前缀和 | O(n) | O(1) | O(n + m) |
如果 n = 10⁶, q = 10⁶,暴力解法:最多需要 10¹² 次操作,而前缀和:只需要 2 × 10⁶ 次操作。
于是我们可以得出一个结论: 当查询次数较多时,前缀和算法能将时间复杂度从 O(m × n) 优化到 O(n + m),效率非常高。
总结
前缀和的核心作用是快速计算任意区间的元素和:
- 通过预处理构建前缀和数组
- 利用公式
sum[l,r] = prefixSum[r] - prefixSum[l-1]
在 O(1) 时间内计算区间和 - 将多次区间查询的时间复杂度从 O(m×n) 优化到 O(n+m)
以上属个人总结,欢迎各位学者评论指正。