终极跨平台键鼠共享解决方案:Barrier让多台电脑共用一套键盘鼠标
2026/4/29 7:20:45
前缀和与差分是算法中经典的数据预处理技巧,能将数组/矩阵的区间查询、区间修改时间复杂度从O ( n ) O(n)O(n)/O ( n m ) O(nm)O(nm)优化到O ( 1 ) O(1)O(1),是刷题和工程开发中处理区间问题的“利器”。本文从一维到二维,结合Java实战代码,拆解核心逻辑与应用场景。
前缀和与差分视频【共四集视频】
前缀和数组preSum中,preSum[i]表示原数组前i个元素的和。通过预处理前缀和,任意区间[L, R]的和可通过preSum[R] - preSum[L-1]直接计算(索引从1开始,避免边界处理)。
importjava.util.Scanner;// 一维前缀和核心实现(索引1开始)publicclassPrefixSum1D{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intn=sc.nextInt();// 数组长度intq=sc.nextInt();// 查询次数int[]arr=newint[n+1];// 原数组(1-based)// 输入原数组for(inti=1;i<=n;i++){arr[i]=sc.nextInt();}// 构建前缀和数组int[]preSum=newint[n+1];for(inti=1;i<=n;i++){preSum[i]=preSum[i-1]+arr[i];}// 处理查询for(inti=0;i<q;i++){intL=sc.nextInt();intR=sc.nextInt();intsum=preSum[R]-preSum[L-1];System.out.println(sum);}sc.close();}}差分是前缀和的逆运算,差分数组diff标记区间修改的“起点”和“终点”:对[L, R]加k,只需diff[L] += k、diff[R+1] -= k(多开一位避免越界),最后对diff求前缀和即可还原修改后的数组。
// 一维差分核心实现(索引1开始)importjava.util.Arrays;importjava.util.Scanner;publicclassDifference1D{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intn=sc.nextInt();// 数组长度intm=sc.nextInt();// 操作次数int[]diff=newint[n+2];// 差分数组(多开1位避免越界)// 处理区间修改操作for(inti=0;i<m;i++){intL=sc.nextInt();intR=sc.nextInt();intk=sc.nextInt();diff[L]+=k;diff[R+1]-=k;}// 还原数组(对差分数组求前缀和)int[]res=newint[n];intcur=0;for(inti=1;i<=n;i++){cur+=diff[i];res[i-1]=cur;}// 输出结果System.out.println(Arrays.toString(res).replaceAll("[\\[\\],]",""));sc.close();}}二维前缀和数组preSum[i][j]表示以(1,1)为左上角、(i,j)为右下角的矩形和。递推公式需补偿重复累加的区域,子矩形和通过“大减小、加补偿”计算。
importjava.util.Scanner;// 二维前缀和核心实现(索引1开始)publicclassPrefixSum2D{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intn=sc.nextInt();// 行数intm=sc.nextInt();// 列数intq=sc.nextInt();// 查询次数// 输入矩阵(1-based)int[][]matrix=newint[n+1][m+1];for(inti=1;i<=n;i++){for(intj=1;j<=m;j++){matrix[i][j]=sc.nextInt();}}// 构建二维前缀和数组int[][]preSum=newint[n+1][m+1];for(inti=1;i<=n;i++){for(intj=1;j<=m;j++){preSum[i][j]=preSum[i-1][j]+preSum[i][j-1]-preSum[i-1][j-1]+matrix[i][j];}}// 处理查询for(inti=0;i<q;i++){intx1=sc.nextInt();inty1=sc.nextInt();intx2=sc.nextInt();inty2=sc.nextInt();// 子矩形和公式:大减小 + 补偿intsum=preSum[x2][y2]-preSum[x1-1][y2]-preSum[x2][y1-1]+preSum[x1-1][y1-1];System.out.println(sum);}sc.close();}}二维差分通过4个标记点实现子矩形批量修改:对(x1,y1)-(x2,y2)加k,只需修改diff[x1][y1]、diff[x1][y2+1]、diff[x2+1][y1]、diff[x2+1][y2+1],最后对diff求二维前缀和还原矩阵。
importjava.util.Scanner;// 二维差分核心实现(索引1开始)publicclassDifference2D{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intn=sc.nextInt();// 行数intm=sc.nextInt();// 列数intk=sc.nextInt();// 操作次数// 差分数组(多开1行1列,避免越界)int[][]diff=newint[n+2][m+2];// 处理子矩形修改操作for(inti=0;i<k;i++){intx1=sc.nextInt();inty1=sc.nextInt();intx2=sc.nextInt();inty2=sc.nextInt();intval=sc.nextInt();// 二维差分4个标记点diff[x1][y1]+=val;diff[x1][y2+1]-=val;diff[x2+1][y1]-=val;diff[x2+1][y2+1]+=val;}// 还原矩阵(对差分数组求二维前缀和)int[][]res=newint[n+1][m+1];for(inti=1;i<=n;i++){for(intj=1;j<=m;j++){// 求前缀和res[i][j]=res[i-1][j]+res[i][j-1]-res[i-1][j-1]+diff[i][j];// 输出当前行(去掉最后一列的空格)System.out.print(res[i][j]+(j==m?"\n":" "));}}sc.close();}}| 技巧 | 核心操作 | 时间复杂度(预处理/单次操作) |
|---|---|---|
| 一维前缀和 | 区间和查询 | O ( n ) O(n)O(n)/O ( 1 ) O(1)O(1) |
| 一维差分 | 区间批量修改 | O ( n ) O(n)O(n)/O ( 1 ) O(1)O(1) |
| 二维前缀和 | 子矩形和查询 | O ( n m ) O(nm)O(nm)/O ( 1 ) O(1)O(1) |
| 二维差分 | 子矩形批量修改 | O ( n m ) O(nm)O(nm)/O ( 1 ) O(1)O(1) |