304. 二维区域和检索 - 矩阵不可变

304. 二维区域和检索 - 矩阵不可变

解题思路:

这道题目很显然不能够用暴力解法。但是一时间想不到更好的办法。
想起昨天一道类似的区域求和问题,我用的是前缀和的方法解决的。
这道题目是二维的,可以用前缀和的方法吗?
首先直接对二维数组进行前缀和统计肯定是困难的。那么我尝试将二维数组转换为一维数组进行统计前缀和。
为了计算的方便,我们生成一个m行n+1列的矩阵。矩阵第一列统一为0.按照行为单位,统计每一行的前缀和。
这样我们根据题目的输入条件,首先限定矩阵行的范围,然后利用前缀和数组,加和求解每一行。

class NumMatrix {
    vector<vector<int> > rowPreSum;  //以行为单位统计前缀和
public:
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        if(m > 0){
            int n = matrix[0].size();
            vector<int> vc;
            for(int i=0;i<m;i++){   //统计前缀和
                int sum = 0;
                
                vc.push_back(0);    //便于计算
                for(int j=0;j<n;j++){
                    sum += matrix[i][j];
                    vc.push_back(sum);
                }
                rowPreSum.push_back(vc);
                vc.clear();
            }
        }

    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        int ans = 0;
        for(int i = row1;i<=row2;i++){  //首先限定行的范围
            ans += (rowPreSum[i][col2+1] - rowPreSum[i][col1]); //根据简单的数学规律进行求解
        }
        return ans;
    }
};
复杂度分析:
  • 时间复杂度:初始化O(mn)。初始化遍历矩阵求解前缀和。计算时候遍历二维区域不超过m行,每次遍历求解时间复杂度O(1)。所以O(mn) + O(m) = O(mn)
  • 空间复杂度:创建二维矩阵O(mn)。

解题思路二:二维前缀和

方法一虽然利用了前缀和,但是每次检索的时间复杂度是 O(m)O(m),仍然没有降到 O(1)O(1)。为了将每次检索的时间复杂度降到 O(1)O(1),需要使用二维前缀和,在初始化的时候计算二维前缀和数组。
我们先来看个例子。
假设已有矩阵如下:

1 2 3
4 5 6
7 8 9

我们统计前缀和如下:

0 0 0 0
0 1 3 6
0 5 12 21
0 12 27 45
前缀和如何计算?首先 生成m+1行n+1列的矩阵,0行0列默认元素为0.这样便于后续计算。
那么则有:preSum[i+1][j+1] = preSum[i][j+1] + preSum[i+1][j] - preSum[i][j](减去重复的)
相应的计算特定范围的加和也有数学公式
计算(1,1)到(2,2)如下
preSum[3][3] - preSum[1][3] - preSum[3][1] + preSum[1][1] = 28
(x1,y1)到(x2,y2)计算如下
preSum[x1+1][y1+1] - preSum[x1][y2+1] - preSum[x2][y1] + preSum[x1][x1]
代码如下:
class NumMatrix {
    vector<vector<int> > preSum;  //二维前缀和
public:
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        if(m > 0){
            int n = matrix[0].size();
            preSum.resize(m+1,vector<int>(n+1)); //增添一行一列0元素,便于计算
            for(int i=0;i<m;i++){   //统计前缀和
                for(int j=0;j<n;j++){
                    preSum[i+1][j+1] = preSum[i][j+1] + preSum[i+1][j] - preSum[i][j] + matrix[i][j];
                }
            }
        }

    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        int ans = 0;
        ans = preSum[row2+1][col2+1] - preSum[row1][col2+1] - preSum[row2+1][col1] + preSum[row1][col1];
        return ans;
    }
};
posted @ 2021-03-02 16:05  focusDing  阅读(82)  评论(0编辑  收藏  举报