leetcode-前缀和数组&差分数组

1. 前缀和数组:

前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和。(仅仅适用于原数组不变的情况,如果原数组经常修改,则需要考虑差分数组。)

模版如下:

复制代码
class PrefixSum {
    // 前缀和数组
    private int[] preSum;

    /* 输入一个数组,构造前缀和 */
    public PrefixSum(int[] nums) {
        // preSum[0] = 0,便于计算累加和
        preSum = new int[nums.length + 1];
        // 计算 nums 的累加和
        for (int i = 1; i < preSum.length; i++) {
            preSum[i] = preSum[i - 1] + nums[i - 1];
        }
    }
    
    /* 查询闭区间 [left, right] 的累加和 */
    public int sumRange(int left, int right) {
        return preSum[right + 1] - preSum[left];
    }
}
复制代码

 

 

看两道例题就清楚了:

I. 303. 区域和检索 - 数组不可变 - 力扣(LeetCode)

 由于要频繁计算某个区间内的元素之和,暴力解法复杂度太大,显然前缀和比较合适,代码如下:

复制代码
class NumArray {
    //前缀和数组
    int[] sumArr;

    public NumArray(int[] nums) {
        sumArr = new int[nums.length + 1];
        for(int i = 1; i < sumArr.length; i++){
            sumArr[i] = sumArr[i - 1] + nums[i - 1];
        }
    }
    
    public int sumRange(int left, int right) {
        //计算[i, j]范围内的和是用sumArr[j + 1] - sumArr[i]计算
        return sumArr[right + 1] - sumArr[left];
    }
}

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray obj = new NumArray(nums);
 * int param_1 = obj.sumRange(left,right);
 */
复制代码

 

II. 304. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)

这道题将前缀和数组扩展到了二维:

在构建前缀和数组时,可以定义前缀和数组(i, j)的值为原矩阵到由(0, 0)到(i - 1, j - 1)构成的子矩阵内部的元素之和,任意矩阵都可以由几个子矩阵的加减运算得到,原理如下图:

 代码如下:

复制代码
class NumMatrix {
    int[][] sumArr;

    public NumMatrix(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        sumArr = new int[m + 1][n + 1];
        //初始化前缀和数组
        for(int i = 1; i < m + 1; i++){
            for(int j = 1; j < n + 1; j++){
                sumArr[i][j] = sumArr[i][j - 1] + sumArr[i - 1][j] + matrix[i - 1][j - 1] - sumArr[i - 1][j - 1];
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        // System.out.println("------");
        // for(int[] row : sumArr){
        //     System.out.println(Arrays.toString(row));
        // }
        return sumArr[row2 + 1][col2 + 1] - (sumArr[row2 + 1][col1] + sumArr[row1][col2 + 1] - sumArr[row1][col1]);
    }
}

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix obj = new NumMatrix(matrix);
 * int param_1 = obj.sumRegion(row1,col1,row2,col2);
 */
复制代码

 

 2. 差分数组

差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。

原理也很简单,如下图所示,原数组是nums,构建差分数组diff时,diff[0] = nums[0],diff[i] = nums[i] - nums[i - 1] when i > 0。

假设我们要更改区间[i, j]的元素,使区域内元素都加3,我们只需要修改差分数组,使diff[i] += 3,diff[j + 1] -= 3,即可,非常方便。

 模板如下:

复制代码
// 差分数组工具类
class Difference {
    // 差分数组
    private int[] diff;
    
    /* 输入一个初始数组,区间操作将在这个数组上进行 */
    public Difference(int[] nums) {
        assert nums.length > 0;
        diff = new int[nums.length];
        // 根据初始数组构造差分数组
        diff[0] = nums[0];
        for (int i = 1; i < nums.length; i++) {
            diff[i] = nums[i] - nums[i - 1];
        }
    }

    /* 给闭区间 [i, j] 增加 val(可以是负数)*/
    public void increment(int i, int j, int val) {
        diff[i] += val;
        if (j + 1 < diff.length) {
            diff[j + 1] -= val;
        }
    }

    /* 返回结果数组 */
    public int[] result() {
        int[] res = new int[diff.length];
        // 根据差分数组构造结果数组
        res[0] = diff[0];
        for (int i = 1; i < diff.length; i++) {
            res[i] = res[i - 1] + diff[i];
        }
        return res;
    }
}
复制代码

 

差分数组题目:1109. 航班预订统计 - 力扣(LeetCode)1094. 拼车 - 力扣(LeetCode)

 
posted @   Vege_dog  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示