持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
前言
今天做了力扣的一道题目,一位只是简单的双指针,但后续发现,有点问题,看过题解后,发现适合使用前缀和,奈何之前对前缀和没什么了解,这里就来简单的介绍一下
例题
数组不可变
链接:303. 区域和检索 - 数组不可变 - 力扣(LeetCode)
代码:
class NumArray {
preSum:number[] = [0];
constructor(nums: number[]) {
//首先设置一个前缀和数组
let sum = 0
for(let value of nums){
sum+=value
this.preSum.push(sum)
}
}
sumRange(left: number, right: number): number {
return this.preSum[right+1]-this.preSum[left]
}
}
这是一个设计题,用到的思想就是前缀和思想
其实结合题目的意思,我们可以理解出,其实我们就是需要求出,left到right之间的sum
一般我们会怎么做呢?
sumRange(0, 2) = num[0]+num[1]+num[2]
sumRange(2, 5) = num[2]+ num[3]+ num[4]+ num[5]
但是如果我们转化一下,你看
sumRange(0, 2) = sumRange(0,2)-sumRange(0,0)
sumRange(2, 5) = sumRange(0,5)-sumRange(0,2)
这么一理解的话,假如我们知道sumRange(0,num[i]),那我们计算这道题,不是很简单嘛?
所以我们就用一个数组,记录所有的sumRange(0,num[i]):preSum:number[] = [0];
但是,你可能会疑惑,为什么第一个是0,而不是num[0]呢?
我的理解是:sumRange(0,0):前0个,这肯定是就是0
记住:preSum[i] 记录的其实就是num[0 ... i-1]的和
你可能又会有疑惑,那为啥是preSum[right+1]-preSum[left]
假如你要获取num[2]的前缀和,你回去找preSum里面的第几个?
答:第三个,为什么,因为:preSum[i] 记录的其实就是num[0 ... i-1]的和
矩阵不可变
链接:304. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
代码:
class NumMatrix {
// 设置一个前缀和数组
preSum:number[][] = []
constructor(matrix: number[][]) {
let m = matrix.length;
let n = matrix[0].length;
if(m === 0 || n=== 0){
return;
}
for (let i = 0; i <= m; i++) {
this.preSum[i]=[]
for (let j = 0; j <= n; j++) {
this.preSum[i][j]=0
}
}
for(let i = 1;i<=m;i++){
for(let j = 1;j<=n;j++){
this.preSum[i][j] = this.preSum[i-1][j]+this.preSum[i][j-1]+matrix[i-1][j-1]-this.preSum[i-1][j-1]
}
}
}
sumRegion(row1: number, col1: number, row2: number, col2: number): number {
return this.preSum[row2+1][col2+1]-this.preSum[row1][col2+1]-this.preSum[row2+1][col1]+this.preSum[row1][col1]
}
}
可能你会有点懵,那么我们来理解一下,和之前的那个数组不可变类似
我们也是需要一个preSum,preSum[i,j]:记录的是region(0,0,i-1,j-1)的和
首先,我们先展示出数组
假设我们现在要求出range(2,1,4,3),那么我们确实可以一次次的去遍历找出
但是其实我们有一个更好的办法:
- 我们要求的是蓝色区域,我们可以用整个那个区域
range(0,0,4,3) - 减去橙色区域
range(0,0,1,3) - 减去黄色区域
range(0,0,4,0) - 加上绿色区域
range(0,0,1,0):因为我们减了两次,所以需要补一次回来
那么对应我们的range(int row1, int col1, int row2, int col2)
我们将减去的其实就是
- 减去橙色区域
range(0,0,row1-1,col2) - 减去黄色区域
range(0,0,row2,col1-1) - 加上绿色区域
range(0,0,row1-1,col1-1)
现在我们来求一下前缀和
对着刚刚得出得出来的结论
- 减去橙色区域前缀和
presum(2,4) - 减去黄色区域前缀和
preSum(5,1) - 加上绿色区域前缀和
preSum(2,1)
那么对应我们的range(int row1, int col1, int row2, int col2)
我们将减去的其实就是
- 减去橙色区域
preSum(row1-1+1,col2+1) - 减去橙色区域
preSum(row2+1,col1-1+1) - 加上绿色区域
preSum(row1-1+1,col1-1+1)
那么这样你就能理解这道题目的意思
我觉得,这就像高中的那种割补法
总结
当然这两道题是不够的,后续再多做题吧,多总结吧
后续慢慢补充这个