前缀和(Prefix Sum)
前缀和的详细解释
- 前缀和:前缀和是一种数组处理技巧,用于快速计算数组中某一段连续子数组的和。它通过将数组中每个位置的值依次累加,得到一个新的数组,这个新数组的第i个元素就是原数组前i个元素的和。
- 前缀和数组:前缀和数组是一个与原数组长度相同的数组,其中第i个元素存储了原数组从索引0到索引i的所有元素的和。
前缀和有什么用
- 快速计算子数组和:通过前缀和数组,可以在O(1)的时间复杂度内计算任意子数组的和,而不需要每次都重新遍历计算。
- 解决一些子数组和的问题:前缀和可以用于解决一些子数组和相关的问题,如最大子数组和、连续子数组和等。
前缀和的示例Java代码
下面是一个简单的Java代码示例,演示如何计算数组的前缀和:
public class PrefixSum {
// 计算数组的前缀和
public static int[] calculatePrefixSum(int[] nums) {
int n = nums.length;
int[] prefixSum = new int[n];
prefixSum[0] = nums[0];
for (int i = 1; i < n; i++) {
prefixSum[i] = prefixSum[i - 1] + nums[i];
}
return prefixSum;
}
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5};
// 计算前缀和数组
int[] prefixSum = calculatePrefixSum(nums);
// 输出前缀和数组
for (int num : prefixSum) {
System.out.print(num + " ");
}
}
}
进阶前缀和哈希等
- 前缀和的哈希优化:对于一些问题,我们可能需要求解子数组和等相关问题时,通过哈希表优化前缀和的计算,可以进一步提高效率。
- 前缀和的应用:前缀和不仅可以用于求解子数组和等问题,还可以应用于解决一些其他的问题,如区间和查询、区间更新等。
例题
解题教程:利用前缀和求解两两相乘再相加的和
理解题目
题目要求给定n个整数,求它们两两相乘再相加的和。具体地,对于给定的n个整数a1, a2, …, an,需要计算以下形式的和:
S = a 1 ⋅ a 2 + a 1 ⋅ a 3 + ⋯ + a 1 ⋅ a n + a 2 ⋅ a 3 + ⋯ + a n − 2 ⋅ a n − 1 + a n − 2 ⋅ a n + a n − 1 ⋅ a n S = a_{1} \cdot a_{2} + a_{1} \cdot a_{3} + \cdots + a_{1} \cdot a_{n} + a_{2} \cdot a_{3} + \cdots + a_{n-2} \cdot a_{n-1} + a_{n-2} \cdot a_{n} + a_{n-1} \cdot a_{n} S=a1⋅a2+a1⋅a3+⋯+a1⋅an+a2⋅a3+⋯+an−2⋅an−1+an−2⋅an+an−1⋅an
解法一
我们可以利用前缀和来解决这个问题。具体步骤如下:
- 计算数组的前缀和数组prefixSum[],其中prefixSum[i]表示前i个元素的和。
- 对于每个数a[i],它与a[0]相乘的结果在前缀和数组中的位置为i+1,它与a[1]相乘的结果在前缀和数组中的位置为i+2,以此类推。
- 遍历数组,对于每个数a[i],将其与a[i+1]到a[n-1]的前缀和相乘,累加到结果中。
Java代码实现
下面是使用Java实现的代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取数组大小
int n = scanner.nextInt();
// 读取数组元素
int[] nums = new int[n];
for (int i = 0; i < n; i++) {
nums[i] = scanner.nextInt();
}
// 计算前缀和数组
long[] prefixSum = new long[n + 1];
prefixSum[0] = 0;
for (int i = 1; i <= n; i++) {
prefixSum[i] = prefixSum[i - 1] + nums[i - 1];
}
// 计算结果
long sum = 0;
for (int i = 0; i < n; i++) {
sum += nums[i] * (prefixSum[n] - prefixSum[i]);
}
// 输出结果
System.out.println(sum);
scanner.close();
}
}
时间复杂度分析
- 计算前缀和数组的时间复杂度为O(n),遍历数组计算结果的时间复杂度为O(n),因此总的时间复杂度为O(n)。
- 空间复杂度为O(n),主要用于存储前缀和数组。
解法二
"后缀和“
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int arr[] = new int[n];
long sumarr[] = new long[n];
long sum = 0;
for (int i = 0 ;i < n;i++) {
arr[i] = scanner.nextInt();
}
for(int i = n-1; i>0; i--) {
if(i == n-1) sumarr[i] = arr[i];
else sumarr[i] = sumarr[i+1] + arr[i];
}
for(int i = 0; i < n-1; i ++) {
sum += arr[i] * sumarr[i+1];
}
System.out.println(sum);
scanner.close();
}
}