[LC] 308. Range Sum Query 2D - Mutable

这一题是https://blog.csdn.net/chaochen1407/article/details/86572593 和 https://blog.csdn.net/chaochen1407/article/details/86610960的共同延伸。在2d - immutable里面,我们的做法的核心是记录每一个从(0, 0)开始到某点的区域面积,然后返回类似return sum[right][Top] - sum[left][Top] - sum[right][Bot] + sum[left][Bot];即可。而1d - mutable里面,我们binary index tree的做法是可以返回 0 ~ i的和 prefixSum(i),然后求range(i ,j) 就是prefixSum(j) - prefixSum(i - 1)。所以到了2d-mutable这里,我们把两者结合一下就好了。嗯,我们可以假想一下,如果binary index tree也可以2d化,然后我们可以 通过一个相对快速的方式拿到bitTreeSum(i, j) 对应2d-immutable里面的 sum[i][j]。那就可以了。这就是2d - binary index tree的基本思路。

但是!最开始,我还是想举出另一个相对简单的解法。

这个解法依旧是在update和建树的时候缓存某些东西cols,cols是一个二维数组,大小和这个2d array一样大,cols[i][j]存的东西就是matrix[i][0] ...  matrix[i][j]的和。所以每次update(row, col, val)的时候,都要进行一个o(n)的操作去更新cols[row][0]... cols[row][col]。这样的话,当求和的时候sumRegion(row1, col1, row2, col2)的时候,所要做的就很简单了,就是求(col[row1][col2] - col[row1][col1 - 1]) + (col[row1+ 1][col2] - col[row1 + 1][col1 - 1]) + .. (col[row2][col2] - col[row2][col1 - 1)即可,这个过程可以认为是O(m)。

根据上述描述,可以得到比较简单的代码如下:

    int[][] cols;
    public NumMatrix(int[][] matrix) {
        if (matrix.length == 0) return;

        this.cols = new int[matrix.length][matrix[0].length];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                this.cols[i][j] = matrix[i][j];
                if (j != 0) {
                    this.cols[i][j] += this.cols[i][j - 1];
                }
            }
        }
    }
    
    public void update(int row, int col, int val) {
        int originValue = this.cols[row][col];
        if (col > 0) originValue -= this.cols[row][col - 1];
        int delta = val - originValue;
        for (int i = col; i < this.cols[row].length; i++) {
            this.cols[row][i] += delta;
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        int result = 0;
        for (int i = row1; i <= row2; i++) {
            result += this.cols[i][col2];
            if (col1 > 0) {
                result -= this.cols[i][col1 - 1];
            }
        }
        
        return result;
    }

介绍完简单的就来难的。回到我们之前所说的2D binary index tree。整体操作基本就是1d binary index tree是二维for循环。在1d binary index tree的时候,bitArr[i]的子节点是bitArr[i + (i & -i)],父亲节点是bitArr[i - (i & -i)]。而对于一个2d binary index tree来说,bitArr[row][col]的父子节点的结构有点难理解,你可以认为遍历完一个bitArr[row][col]的所有子孙节点就是分两个维度在跑一维的binary index tree。所以就是一个二维for循环,外循环在跑row + (row & -row),内循环在跑col + (col & -col)。这样就能遍历完bitArr[row][col]的子孙节点了。求和和一维binary index tree也是一样的,可以直接求到的是以(0, 0)为起点到(row, col)的区域和,我们称之为getAreaSum。同样跑的是一个二维for循环,外层是row - (row & -row), 里层是 col - (col & -col),把所有遍历过的节点全加起来即可。而这里的getAreaSum(row, col),其实就等同于2d-immutable里面解法的sum[row][col],只是复杂度是O(logmn),而不是o(1)。所以sumRegion(row1, col1, row2, col2)其实就是getAreaSum(row2, col2) - getAreaSum(row1 - 1, col2) - getAreaSum(row2, col1 - 1) + getAreaSum(row1 -1, col1 - 1)。和1维的binary index tree的建树是一样的,就相当于对每个节点call一个update,这个update的delta值就是matrix里面的值。我尝试过像1维的binary index tree那样把建树的复杂度优化成O(mn)但是我失败了,所以建树的算法复杂度目前就是O(mn * logmn)。

根据上述描述,可以得到代码是这样的

    int[][] bitArr;
    int[][] origin;

    public NumMatrix(int[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) return;
        this.origin = new int[matrix.length][matrix[0].length];
        this.bitArr = new int[matrix.length + 1][matrix[0].length + 1];

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                this.update(i, j, matrix[i][j]);
            }
        }
    }
    
    public void update(int row, int col, int val) {
        int delta = val - this.origin[row][col];
        this.origin[row][col] = val;
        for (int i = row + 1; i < this.bitArr.length; i = i + (i & -i)) {
            for (int j = col + 1; j < this.bitArr[0].length; j = j + (j & -j)) {
                this.bitArr[i][j] += delta;
            }
        }
    }
    
    public int sumRegion(int row1, int col1, int row2, int col2) {
        return this.getAreaSum(row2, col2) - this.getAreaSum(row1 - 1, col2)
            - this.getAreaSum(row2, col1 - 1) + this.getAreaSum(row1 - 1, col1 - 1);
    }
    
    public int getAreaSum(int row, int col) {
        int result = 0;
        for (int i = row + 1; i > 0; i = i - (i & -i)) {
            for (int j = col + 1; j > 0; j = j - (j & -j)) {
                result += this.bitArr[i][j];
            }
        }
        
        return result;
    }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值