> 文章列表 > 前缀和技巧解决数组问题

前缀和技巧解决数组问题

前缀和技巧解决数组问题

前缀

  • 1. 前缀和
    • 1.1 一维数组
    • 1.2 二维数组:
  • 2. 使用哈希表存储前缀和

1. 前缀和

1.1 一维数组:

  • 输入数组为nums,其长度为n=nums.length;
  • 构建前缀和数组preSum,其长度为n+1,其中preSum[i]=preSum[i-1]+nums[i-1],表示nums[0]~nums[i-1]之和,preSum[0]=0
  • 区间[left,right]内元素和可通过preSum[right+1]-preSum[left]得到

在这里插入图片描述
力扣303:区域和检索 - 数组不可变
给定一个整数数组 nums,处理以下类型的多个查询:

计算索引 left 和 right (包含 left 和 right)之间的 nums 元素的 和 ,其中 left <= right
实现 NumArray 类:

  • NumArray(int[] nums) 使用数组 nums 初始化对象;
  • int sumRange(int i, int j) 返回数组nums 中索引 left 和 right 之间的元素的 总和 ,包含 left 和 right 两点(也就是 nums[left] +
    nums[left + 1] + … + nums[right] );
    // 前缀和int[] preSum;public NumArray(int[] nums) {// 构建前缀和数组this.preSum=new int[nums.length+1];for(int i=1;i<=nums.length;i++){preSum[i]=preSum[i-1]+nums[i-1];}}public int sumRange(int left, int right) {return preSum[right+1]-preSum[left];}

1.2 二维数组:

  • 输入数组为matrix,其行数、列数分别为度为m=matrix.length、n=matrix[0].length;
  • 构建前缀和数组preSum[m+1][n+1],其中preSum[i][j]=preSum[i-1][j]+preSum[i][j-1]-preSum[i-1][j-1]+matrix[i-1][j-1],表示matrix[0][0]~matrix[i-1][j-1]区域元素之和,preSum[0][j]=0且preSum[i][0]=0
    在这里插入图片描述
  • 左上角[r1,c1]到右下角[r2,c2]构成的区域内元素和可通过preSum[r2+1][c2+1]-preSum[r1][c2+1]-preSum[r2+1][c1]+preSum[r1][c1]得到
    在这里插入图片描述
    力扣304 :二维区域和检索 - 矩阵不可变
    给定一个二维矩阵 matrix,以下类型的多个请求:
    计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。
    实现 NumMatrix 类:
  • NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化 ;
  • int sumRegion(int row1,int col1, int row2, int col2) 返回 左上角 (row1, col1) 、右下角 (row2, col2)
    所描述的子矩阵的元素总和。
    int[][] preSum;public NumMatrix(int[][] matrix) {int m=matrix.length;int n=matrix[0].length;// 构建前缀和数组this.preSum=new int[m+1][n+1];for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){preSum[i][j]=preSum[i-1][j]+preSum[i][j-1]-preSum[i-1][j-1]+matrix[i-1][j-1];}}}public int sumRegion(int row1, int col1, int row2, int col2) {return preSum[row2+1][col2+1]-preSum[row1][col2+1]-preSum[row2+1][col1]+preSum[row1][col1];}

2. 使用哈希表存储前缀和

力扣560: 和为 K 的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。

    // 子区间元素和为k// preSum[j+1]-preSum[i]=k// 方式一:前缀和+枚举
//    public int subarraySum(int[] nums, int k) {
//        int n = nums.length;
//        // 构建前缀和数组
//        int[] preSum=new int[n+1];
//        for(int i=1;i<=n;i++){
//            preSum[i]=preSum[i-1]+nums[i-1];
//        }
//        // 枚举
//        int count=0;
//        for(int i=0;i<n;i++){  // 子区间左端
//            for(int j=i;j<n;j++){  // 子区间右端
//                if(preSum[j+1]-preSum[i]==k){
//                    count++;
//                }
//            }
//        }
//        return count;
//    }// 方式二:使用哈希表存储前缀和以及该前缀和出现的次数public int subarraySum(int[] nums, int k) {HashMap<Integer,Integer> map=new HashMap<>();map.put(0,1);  // preSum_0=0int preSum_i=0;  // 表示nums[0]~表示nums[i-1]之和int count=0;for (int i=1;i<=nums.length;i++){preSum_i+=nums[i-1];  // 计算前缀和preSum_i,表示nums[0]~表示nums[i-1]之和int preSum_j=preSum_i-k;  // 以nums[i-1]结尾的子数组,尝试确定子数组的左端位置对应的前缀和// 查看是否有满足条件的“左端“对应前缀和存在if (map.containsKey(preSum_j)){count+=map.get(preSum_j);}// 记录该前缀和值出现的次数map.put(preSum_i,map.getOrDefault(preSum_i,0)+1);}return count;}