欢迎关注个人主页:逸狼
创造不易,可以点点赞吗~
如有错误,欢迎指出~
一维前缀和

解法
理解题意
下标从1开始计,如示例1:
- 输入第一行3 2 表示数组长度n=3,要询问q=2次,
- 第二行1 2 4 表示该数组为1, 2, 4,
- 第三行表示第一次查询,要算下标为1到2对应数的和,
- 第四行表示第二次查询 要计算下标为2到3对应数的和
暴力解法O(n* q): 直接根据题意加和
解法二: 前缀和(快速求出数组中某一个连续区间的和) O(q) + O(n) 用空间换时间
- 预处理出来一个前缀和数组dpi ,该数组表示1, i区间内所有元素的和(比如dp2表示前2个数的和, dp3是前3个数的和...) dpi = dpi - 1 + arri , arr是原数组
- 使用前缀和数组 若要计算l, r区间的和, 直接dpr - dpl - 1
细节处理: 不从0下标开始是因为如果要计算0到1的和时,会出现dp1 - dp-1的情况,越界了 . 假设要计算1到2的和,则有dp2 - dp0, 将dp0初始化为0就行了
画图举例

代码
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
//1.读入数据
int n = in.nextInt(), q = in.nextInt();
int[] arr = new int[n + 1];
for(int i = 1; i <= n; i++) arr[i] = in.nextInt();
//2.预处理一个前缀和数组
long[] dp = new long[n + 1];//防止溢出
for(int i = 1; i <= n; i++) dp[i] = dp[i - 1] + arr[i];
//3.使用前缀和数组
while(q > 0){
int l = in.nextInt(), r = in.nextInt();
System.out.println(dp[r] - dp[l - 1]);
q--;
}
}
}
二维前缀和

解法
理解题意: 输入第1行 3 4 3 表示n = 3行, m = 4列的二维数组,要查询q = 3次
第2行到第4行表示该二维数组
后面3行分别表示3次查询,如第5行 1 1 2 2表示要算出(1, 1)为左上角,(2, 2)为右下角的二维数组的和,6和7行以此类推...

暴力解法: 直接求和,(最差情况是q次询问的每次都要求 求整个区间的和,每次都要遍历一遍数组,所以时间复杂度是: O(n * m * q) --> 会超时)
解法二: 前缀和 (时间复杂度: O(m * n) + O(q) )
- 预处理出来一个前缀和矩阵dpij ,表示从1, 1为左上角,i, j为右下角组成的二维数组所有元素的和, 将arr数组分为A B C D四个部分,利用面积关系得出 dpij = dpi - 1j + dpij - 1 + arrij - dpi - 1j - 1
- 使用前缀和矩阵
画图举例

代码
import java.util.Scanner;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
//1.读入数据
int n = in.nextInt(), m = in.nextInt(), q = in.nextInt();
int[][] arr = new int[n + 1][m + 1];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
arr[i][j] = in.nextInt();
}
}
//预处理一个前缀和矩阵
long[][] dp = new long[n + 1][m + 1];
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + arr[i][j] - dp[i - 1][j - 1];
}
}
//使用前缀和矩阵
while(q > 0){
int x1 = in.nextInt(), y1 = in.nextInt(), x2 = in.nextInt(), y2 = in.nextInt();
System.out.println(dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]);
q--;
}
}
}
寻找数组的中心下标

解法
利用前缀和思想: 定义一个前缀和fi,和一个后缀和gi
- 前缀和 fi 表示0, i - 1区间所有元素的和 , fi = fi - 1 + numsi - 1
- 后缀和 gi 表示 i + 1, n - 1区间所有元素的和 , gi = gi + 1 + numsi + 1

列举下标0 ~ n - 1的中判断fi == gi
细节问题: 初始化f0和gn - 1, f0 表示-1, 0区间的值 即f0=0, gn - 1同理, g(n - 1) = 0;
填表顺序: f表要从左向右填, g表要从右向左填
代码
class Solution {
public int pivotIndex(int[] nums) {
int n = nums.length;
int[] f = new int[n], g = new int[n];
//1.预处理一下前缀和数组以及后缀和数组
for(int i = 1; i < n; i++){
f[i] = f[i - 1] + nums[i - 1];
}
for(int i = n - 2; i >= 0; i--){
g[i] = g[i + 1] + nums[i + 1];
}
//2.使用
for(int i = 0; i < n; i++){
if(f[i] == g[i]) return i;
}
return -1;
}
}
除自身以外数组的乘积

解法
暴力解法: 每次都遍历一遍数组,将除了numsi的元素相乘,时间复杂度O(n^2)
解法二; 前缀积 fi 和后缀积 gi,
- fi表示: 0, i - 1区间内所有元素的乘积, fi = fi - 1 * numsi - 1
- gi表示: i + 1, n - 1区间内所有元素的乘积, gi = gi + 1 * numsi + 1

使用前缀积 : 使用ret数组表示结果, 将fi * gi填入ret的 i 位置
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] f = new int[n], g = new int[n];
//1.预处理一个前缀积数组和后缀积
f[0] = g[n - 1] = 1;
for(int i = 1; i < n; i++){
f[i] = f[i - 1] * nums[i - 1];
}
for(int i = n - 2; i >= 0; i--){
g[i] = g[i + 1] * nums[i + 1];
}
int[] ret = new int[n];
for(int i = 0; i < n; i++){
ret[i] = f[i] * g[i];
}
return ret;
}
}

处理细节: f0 = 1, gn - 1 = 1;
代码
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] f = new int[n], g = new int[n];
//1.预处理一个前缀积数组和后缀积
f[0] = g[n - 1] = 1;
for(int i = 1; i < n; i++){
f[i] = f[i - 1] * nums[i - 1];
}
for(int i = n - 2; i >= 0; i--){
g[i] = g[i + 1] * nums[i + 1];
}
int[] ret = new int[n];
for(int i = 0; i < n; i++){
ret[i] = f[i] * g[i];
}
return ret;
}
}
