目录
【一维】前缀和模版
题目描述
讲解
解法:前缀和 => 快速求出数组中的某一连续区间和.
第一步:预处理出一个前缀和数组 .
第二步:使用前缀和数组
代码实现
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 1.读入数据
int n = in.nextInt(),q = in.nextInt();
int[] nums = new int[n+1];
for(int i = 1; i <= n; i++) {
nums[i] = in.nextInt();
}
//2. 预处理前缀和数组
long[] dp = new long[n+1];
for(int i = 1; i <= n; i++){
dp[i] = dp[i-1] + nums[i];
}
//3.使用前缀和
while(q > 0) {
int l = in.nextInt(),r = in.nextInt();
System.out.println(dp[r]-dp[l-1]);
q--;
}
}
【二维】前缀和模版
题目描述
解释;
讲解
第一步:构造一个前缀和矩阵.
dp[ i ][ j ] :表示从 [1] [1] 到 [ i ] [ j ] ,这段区间里所有元素的和.
第二步:使用前缀和矩阵.
代码实现
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();
}
}
// 2.预处理数据
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--;
}
}
寻找数组的中心下标
题目描述
给你一个整数数组
nums
,请计算数组的 中心下标 。数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。
如果中心下标位于数组最左端,那么左侧数之和视为
0
,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回
-1
。
讲解
解法:前缀和.
从中⼼下标的定义可知,除中⼼下标的元素外,该元素左边的「前缀和」等于该元素右边的「后缀
和」。
- 因此,我们可以先预处理出来两个数组,⼀个表⽰前缀和,另⼀个表⽰后缀和。
- 然后,我们可以⽤⼀个 for 循环枚举可能的中⼼下标,判断每⼀个位置的「前缀和」以及 「后缀和」,如果⼆者相等,就返回当前下标。
细节问题:
数组越界异常.
f[0] = 0 , g[ n -1] = 0.
代码实现
public int pivotIndex(int[] nums) {
int n = nums.length;
int[] f = new int[n];
int[] g = new int[n];
//预处理
for(int i = 1; i < n ; i++) {
f[i] = f[i - 1] + nums[i - 1];
}
for(int j = n - 2; j >= 0; j--) {
g[j] = g[j + 1] + nums[j + 1];
}
for(int i = 0; i < n; i++) {
if(f[i] == g[i]) {
return i;
}
}
return -1;
}
除自身以外数组的乘机
题目描述
讲解
根据题意,对于每⼀个位置的最终结果
ret[i]
,它是由两部分组成的:
- nums[0] * nums[1] * nums[2] * ... * nums[i - 1]
- nums[i + 1] * nums[i + 2] * ... * nums[n - 1]
于是,我们可以利⽤前缀和的思想,使⽤两个数组 f 和 g,分别处理出来两个信息:
- f[ ] 表⽰:i 位置之前的所有元素,即 [0, i - 1] 区间内所有元素的前缀乘积,
- g[ ] 表⽰: i 位置之后的所有元素,即 [i + 1, n - 1] 区间内所有元素的后缀乘积
然后再处理最终结果。
代码实现
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] f = new int[n];
int[] g = new int[n];
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;
}
和为K的子数组
题目描述
讲解
设
i
为数组中的任意位置,⽤
sum[i]
表⽰
[0, i]
区间内所有元素的和。
想知道有多少个「以
i
为结尾的和为
k
的⼦数组」,就要找到有多少个起始位置为
x1, x2,
x3...
使得
[x, i]
区间内的所有元素的和为
k
。那么
[0, x]
区间内的和是不是就是
sum[i] - k
了。于是问题就变成:
- 找到在 [0, i - 1] 区间内,有多少前缀和等于 sum[i] - k 的即可。
代码实现
public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> hash = new HashMap<>();
hash.put(0,1);
int sum = 0,ret = 0;
for(int x : nums) {
sum += x; //当前位置的和
ret += hash.getOrDefault(sum - k,0); // 统计个数
hash.put(sum,hash.getOrDefault(sum,0) + 1); // 前缀和放到哈希表中
}
return ret;
}
1314.矩阵区域和
题目描述
讲解
⼆维前缀和的简单应⽤题,关键就是我们在填写结果矩阵的时候,要找到原矩阵对应区域的「左上
⻆」以及「右下⻆」的坐标(推荐⼤家画图)
- 构造前缀和:是从[1][1]开始的,因此在,原始数组mat找值需要 -1.
- 左上⻆坐标: x1 = i - k,y1 = j - k ,但是由于会「超过矩阵」的范围,因此需要对 0 取⼀个 max 。因此修正后的坐标为: x1 = max(0, i - k), y1 = max(0, j - k) ;
- 右下⻆坐标: x1 = i + k,y1 = j + k ,但是由于会「超过矩阵」的范围,因此需要对 m - 1 ,以及 n - 1 取⼀个 min 。因此修正后的坐标为: x2 = min(m - 1, i + k), y2 = min(n - 1, j + k)
- 在使用dp前缀数组时,则需要进行+1,在确定左、右角下标的基础上进行加一.
代码实现
public int[][] matrixBlockSum(int[][] mat, int k) {
int m = mat.length, n = mat[0].length;
// 1.构造前缀和
int[][] dp = new int[m + 1][n + 1];
//从[1][1]开始计算
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
}
}
// 2. 使用
int[][] ret = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int x1 = Math.max(0, i - k) + 1, y1 = Math.max(0, j - k) + 1;
int x2 = Math.min(m - 1, i + k) + 1, y2 = Math.min(n - 1, j + k) + 1;
ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
}
}
return ret;
}