文章目录
- 一维前缀和
- 一维前缀和概念
- 一维前缀和数组的构建
- 二维前缀和
- 二维前缀和概念
- 二维前缀和数组的构建
- 一维差分
- 一维差分概念
- 一维差分数组的构建
- 二维差分
- 二维差分概念
- 二维差分数组的构建
一维前缀和
一维前缀和概念
一维前缀和是一种常用的数据预处理方法,它能够在O(1)的时间复杂度内快速求出数组任意子区间的和。这种方法通过构建一个前缀和数组来实现,其中每个元素表示从数组开始到当前位置的所有元素的和。
一维前缀和数组的构建
接下来让我们用一道题引入主题
public class Main
{public static void main(String[] args){Scanner in = new Scanner(System.in);int n=in.nextInt();int q=in.nextInt();long[] arr=new long[n+1];long[] pre=new long[n+1];//一维前缀和数组for(int i=1;i<=n;i++){arr[i]=in.nextLong();pre[i]=pre[i-1]+arr[i];//一维前缀和的构建}while((q--)>0){int l=in.nextInt();int r=in.nextInt();System.out.println(pre[r]-pre[l-1]);//一维前缀和的使用:pre[r]-pre[l-1]}}
}
一维前缀和题目链接
一维前缀和的构建就是需要求和的前一个元素的前缀和加上求和元素本身。
切记别死记模板
列如这一道题寻找数组的中心下标
class Solution {public int pivotIndex(int[] nums) {int n=nums.length;int[] lsum=new int[n];int[] rsum=new int[n];lsum[0]=rsum[n-1]=0;for(int i=1;i<n;i++){lsum[i]=lsum[i-1]+nums[i-1];}for(int i=n-2;i>=0;i--){rsum[i]=rsum[i+1]+nums[i+1];}for(int i=0;i<n;i++){if(lsum[i]==rsum[i]){return i;}}return -1;}
}
lsum数组表示的是[0,i-1]区间的前缀和,rsum数组表示的是[i+1,n-1]区间的前缀和。
一维前缀和的使用:pre[r]-pre[l-1]图解
二维前缀和
二维前缀和概念
二维前缀和是一种在二维数组或矩阵中快速计算子矩阵元素和的方法。
二维前缀和数组的构建
在二维数组中,我们首先定义一个二维数组dp,其中dp[i][j]表示从原数组左上角(1,1)到(i,j)形成的子矩阵的元素和。计算dp[i][j]的状态转移方程如下
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + arr[i][j]
这个方程的含义是,当前元素的前缀和等于其上方和左方元素的前缀和之和,减去左上角重叠部分的前缀和,再加上当前元素的值。
图解:
二维前缀和题目模板
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别int n=in.nextInt();int m=in.nextInt();int 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();int y1=in.nextInt();int x2=in.nextInt();int y2=in.nextInt();System.out.println(dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]);}}
}
二维前缀和的使用:dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1])
一维差分
一维差分概念
对于一个数列 a[i],我们需要维护的数据是“相邻两个数之差”。这种策略是,令p[i]=a[i]-a[i-1]即相邻两数的差。我们称数列 p[i] 为数列 a[i]的差分数列。差分数列可以快速的在某个区间加上加上或者减去一个数。
一维差分数组的构建
差分可以看成前缀和的逆运算
首先给定一个原数组a:a[1], a[2], a[3], a[n];
然后我们构造一个数组b : b[1], b[2], b[3], b[i];
使得 a[i] = b[1] + b[2] + b[3] + , + b[i];
1.a[1]=b[1];
2.a[2]=b[1]+b[2];
3.a[3]=b[1]+b[2]+b[3];
由1,2,3得
b[1]=a[1];
b[2]=a[2]-b[1]=a[2]-a[1];
b[3]=a[3]-b[1]-b[2]=a[3]-a[2];
也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。
一维差分的构建用p[i]=a[i]-a[i-1]即可。
一维差分题目模板
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n=in.nextInt();int m=in.nextInt();int[] arr=new int[n+10];for(int i=1;i<=n;i++){arr[i]=in.nextInt();}long[] pre=new long[n+10];for(int i=1;i<=n;i++){pre[i]=arr[i]-arr[i-1];//建立差分数组}while((m--)>0){int l=in.nextInt();int r=in.nextInt();int k=in.nextInt();pre[l]+=k;//使用差分数组pre[r+1]-=k;}for(int i=1;i<=n;i++){pre[i]+=pre[i-1];//差分数组求前缀和还原为原数组System.out.print(pre[i]+" ");}}
}
使用差分数组
pre[l]+=k;
pre[r+1]-=k;
二维差分
二维差分概念
二维差分是一种数据处理技术,应用于二维数组或矩阵中,用来快速计算和更新子矩阵元素的和。 它是对一维差分概念的自然扩展,旨在简化对二维数据结构中特定区域元素进行加减操作的过程,同时保持较高的计算效率。
二维差分数组的构建
由于差分可以看出前缀和的逆运算,所以差分数组可以通过前缀和数组求得。
dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + arr[i][j];//前缀和数组
arr[i][j]=dp[i][j]-dp[i-1][j]-dp[i][j-1]+dp[i-1][j-1];
二维差分题目模板
public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n=in.nextInt();int m=in.nextInt();int q=in.nextInt();long[][] arr1=new long[n+2][m+2];long[][] arr2=new long[n+2][m+2];for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){arr1[i][j]=in.nextLong();//构建二维差分数组arr2[i][j]=arr1[i][j]-arr1[i-1][j]-arr1[i][j-1]+arr1[i-1][j-1];}}while((q--)>0){int x1=in.nextInt();int y1=in.nextInt();int x2=in.nextInt();int y2=in.nextInt();int k=in.nextInt();//二维差分的使用arr2[x1][y1]+=k;arr2[x2+1][y1]-=k;arr2[x1][y2+1]-=k;arr2[x2+1][y2+1]+=k;}long[][] arr3=new long[n+2][m+2];for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){//二维差分数组求前缀和得原数组arr3[i][j]=arr3[i-1][j]+arr3[i][j-1]-arr3[i-1][j-1]+arr2[i][j];System.out.print(arr3[i][j]+" ");}System.out.println();}}
}
//二维差分的使用图解arr2[x1][y1]+=k;arr2[x2+1][y1]-=k;arr2[x1][y2+1]-=k;arr2[x2+1][y2+1]+=k;
原数组通过前缀和数组求差分得来。
public class Main12
{public int[] arr1={0,1,2,3,4,5,6,7,8,9,10};int n=arr1.length;int[] sum=new int[n+1];//一维前缀和数组int[] ppre=new int[n+1];//一维前缀和数组求差分的数组@Overridepublic String toString() {return "Main12{" +"sum=" + Arrays.toString(sum) +", ppre=" + Arrays.toString(ppre) +'}';}public void sum1(int[] arr2)//一维前缀和{for(int i=1;i<n;i++){sum[i]=sum[i-1]+arr2[i];}}public void ppre(int[] arr1){for(int i=1;i<n;i++){ppre[i]=sum[i]-sum[i-1];}}public static void main(String[] args) {Main12 main12=new Main12();main12.sum1(main12.arr1);main12.ppre(main12.sum);System.out.println(main12.toString());}
}
另外通过差分求原数组大家可以自己试试。