1.前缀和
1.1 思想
用一个数组进行存储数组区间之和,涉及到区间和的查询,直接使用前缀和数组进行查询,用空间换时间!
1.2 声明方式
一维前缀和数组申明方式
int []sum = new int[n+1]; sum[0] = 0; // 一般会留出0位置的元素充当边界值 // sum[i]中i表示第几个元素,sum[0]表示没有元素的前缀和 for (int i=1;i<=n;i++){ sum[i+1] = sum[i] + nums[i]; }
1.3 适用范围
适用于数组不做修改的区间累加快速求和!
2.例题
leetcode303
class NumArray {
int []sum ;
public NumArray(int[] nums) {
int len = nums.length;
sum = new int[len];
sum[0] = nums[0];
for (int i=1;i<len;i++){
sum[i] = sum[i-1]+nums[i];
}
}
public int sumRange(int left, int right) {
if(left == 0)return sum[right];
return sum[right] - sum[left-1];
}
}
leetcode304
class NumMatrix {
int [][]sum;
public NumMatrix(int[][] matrix) {
int m = matrix.length;
int n =matrix[0].length;
sum = new int[m+1][n+1];
for (int i=1;i<=m;i++){
for (int j=1;j<=n;j++){
sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + matrix[i-1][j-1];
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
return sum[row2+1][col2+1] - sum[row2+1][col1] -sum[row1][col2+1] + sum[row1][col1];
}
}
leetcode560
用普通的前缀和数组无法进行求解,时间超时!!!
换一种思路,采用前缀和的话,目的是要求出两个前缀和只差等于k,将前缀和+k的值的次数存储在HashMap中,key为前缀和+k,value为出现的次数。只需要进行遍历一次的数组,用当前的前缀和进行判断是否存在有相等的前缀和+k,有的话则需要加上value。
class Solution {
public int subarraySum(int[] nums, int k) {
int len = nums.length;
int res =0;
// map中存储着key表示前缀和,value表示前缀和出现的次数
HashMap<Integer,Integer> map = new HashMap<>();
// key=0,value=1表示,前缀和为0的出现一次
map.put(0,1);
int cv = 0;
// i在右边,j在左边,j对应的前缀和存储在HashMap中,这样就避免了i和j重复的现象
for (int i=0;i<len;i++){
cv += nums[i];
res += map.getOrDefault(cv - k , 0);
map.put(cv,map.getOrDefault(cv,0)+1);
}
return res;
}
}
leetcode327
1.采用普通的前缀和存储,进行遍历所有情况会出现超时的现象。
class Solution { public int countRangeSum(int[] nums, int lower, int upper) { int len = nums.length; long []sum = new long[len]; long cv=0; for (int i=0;i<len;i++){ cv += nums[i]; sum[i] = cv; } int res =0; for (int clen =1;clen<=len;clen++){ for(int i=0;i<=len-clen;i++){ int j = i+clen -1; if(j>=len)break; if(i==0&&sum[j]>=lower&&sum[j]<=upper)res++; if(i!=0&&sum[j]-sum[i-1]>=lower&&sum[j]-sum[i-1]<=upper)res++; } } return res; } }
2.采用归并排序+前缀数组(依旧超时)
package alian; class Solution { public static void main(String[] args) { Solution solution = new Solution(); System.out.println(":"+solution.countRangeSum(new int[]{-2,5,-1}, -2, 2)); // System.out.println(":"+solution.countRangeSum(new int[]{-2147483647,0,-2147483647,2147483647}, -564, 3864)); } int res = 0; int lower; int upper; public int countRangeSum(int[] nums, int lower, int upper) { this.lower=lower; this.upper =upper; int len = nums.length; long []sum = new long[len]; long cv =0; for (int i=0;i<len;i++) { cv = cv + (long)nums[i]; sum[i] = cv; } mergeSort(sum,0,len-1); return res; } public void mergeSort(long []nums,int left,int right){ if(left == right){ if(left == 0 && nums[left]>=lower&&nums[right]<=upper){ res ++; }else if((left != 0 && nums[left] - nums[left-1]>=lower&&nums[left] - nums[left-1]<=upper)){ res ++; } return; } int mid = (left+right)/2; mergeSort(nums,left,mid); mergeSort(nums,mid+1,right); sort(nums,left,mid,right); } public void sort(long []nums,int left,int mid,int right){ long tep[] = new long[right-left+1]; int i = left; int j = mid +1; int index = 0; for (int k=left;k<=mid;k++){ for (int m=mid+1;m<=right;m++){ long value ; if(k == 0){ value = nums[m]; }else { value = nums[m] - nums[k-1]; } if(value>=lower&&value<=upper){ res++; } } } while(i<=mid&&j<=right){ if(nums[i]>nums[j]){ tep[index] = nums[j]; index++; j++; }else { tep[index] = nums[i]; index++; i++; } } while(i<=mid){ tep[index] = nums[i]; index++; i++; } while(j<=right){ tep[index] = nums[j]; index++; j++; } index =0; for ( i =left;i<=right;i++){ nums[i]=tep[index]; index ++; } } }