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