前缀和技巧解决数组问题

前缀和

  • 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]得到

前缀和技巧解决数组问题_第1张图片
力扣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
    前缀和技巧解决数组问题_第2张图片
  • 左上角[r1,c1]到右下角[r2,c2]构成的区域内元素和可通过preSum[r2+1][c2+1]-preSum[r1][c2+1]-preSum[r2+1][c1]+preSum[r1][c1]得到
    前缀和技巧解决数组问题_第3张图片
    力扣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
//            for(int j=i;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=0

        int 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;
    }

你可能感兴趣的:(Java开发,LeetCode,算法,数据结构,leetcode)