有什么不懂的地方欢迎留言!
题目描述
给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (row1, col1) ,右下角为 (row2, col2) 。
上图子矩阵左上角 (row1, col1) = (2, 1) ,右下角(row2, col2) = (4, 3),该子矩形内元素的总和为 8。
示例:
给定 matrix = [
[3, 0, 1, 4, 2],
[5, 6, 3, 2, 1],
[1, 2, 0, 1, 5],
[4, 1, 0, 1, 7],
[1, 0, 3, 0, 5]
]
sumRegion(2, 1, 4, 3) -> 8
sumRegion(1, 1, 2, 2) -> 11
sumRegion(1, 2, 2, 4) -> 12
提示:
- 你可以假设矩阵不可变。
- 会多次调用 sumRegion 方法。
- 你可以假设 row1 ≤ row2 且 col1 ≤ col2 。
来源:力扣(LeetCode):https://leetcode-cn.com/problems/range-sum-query-2d-immutable
解题思路一
根据303. 区域和检索 - 数组不可变述中用到的前缀和方法,可以应用于这一题中!我们可以对这n个一维数组求前缀和,得到n个一维preSum数组。
根据一维前缀和(前n项和、preSum)的定义:
(1)preSum[i]:第 0 项到 第 i 项 的和。
p
r
e
S
u
m
[
i
]
=
n
u
m
s
[
0
]
+
n
u
m
s
[
1
]
+
.
.
.
.
.
.
.
+
n
u
m
s
[
i
]
;
preSum[i]=nums[0]+nums[1]+.......+nums[i];
preSum[i]=nums[0]+nums[1]+.......+nums[i];
(2)部分和sumRange(i,j):第 i 项到第 j 项的和(前 j 项减去前 i-1 项)
s
u
m
R
a
n
g
e
(
i
,
j
)
=
p
r
e
S
u
m
[
j
]
−
p
r
e
S
u
m
[
i
−
1
]
;
sumRange(i,j)=preSum[j]-preSum[i-1];
sumRange(i,j)=preSum[j]−preSum[i−1];
此时当i=0
时,会有 i-1 = -1
,即会有 preSum[-1]
,这就要对 i=0
时进行特殊的处理,毕竟数组下标没有-1
呀!最简单的处理方法就是重新修改preSum定义,如下:
(1)preSum[i]:第 0 项到 第 i-1 项 的和,即不包括第 i 项。
p
r
e
S
u
m
[
i
]
=
n
u
m
s
[
0
]
+
n
u
m
s
[
1
]
+
.
.
.
.
.
.
.
+
n
u
m
s
[
i
−
1
]
;
preSum[i]=nums[0]+nums[1]+.......+nums[i-1];
preSum[i]=nums[0]+nums[1]+.......+nums[i−1];
(2)部分和sumRange(i,j):第 i 项到第 j 项的和(前 j 项减去前 i-1 项)
s
u
m
R
a
n
g
e
(
i
,
j
)
=
p
r
e
S
u
m
[
j
+
1
]
−
p
r
e
S
u
m
[
i
]
;
sumRange(i,j)=preSum[j+1]-preSum[i];
sumRange(i,j)=preSum[j+1]−preSum[i];
此时当 i=0
时,让preSum[0]=0
即可,无需对 i=0
这种情况进行特殊的处理了!(将 preSum数组的列数设为n+1 ,目的是为了方便计算每一行的子数组和,不需要对 i =0 的情况特殊处理。)
举例:根据题目要求,需要计算如下黄色区间的和
按照修改之后的前缀和的定义,我们需要创建一个列数为n+1的preSum数组,并且计算它的前缀和!
然后套用公式就可以算出每一行的部分和,然后把要求的所有行的部分和相加就能求出最终结果!如下图:先求出黄色区域中第一行的部分和(4-1),再求出第二行的部分和(6-4),再求出最后一行的部分和(4-1),把这三行的部分和相加就能得到最终答案了!
代码
#include<iostream>
#include<vector>
using namespace std;
class NumMatrix {
public:
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, vector<int>(n + 1)); //创建 m 行,n+1列的二维数组,并初始化为0!
for (int i = 0; i < m; i++) { //计算每一行的前缀和(即前n项和)
for (int j = 0; j < n; j++) {
Presum[i][j + 1] = Presum[i][j] + matrix[i][j];
}
}
}
}
int sumRegion(int row1, int col1, int row2, int col2) {
int sum = 0;
for (int i = row1; i <= row2; i++) { //计算每一行的部分和,i 表示第 i 行
sum = sum + Presum[i][col2 + 1] - Presum[i][col1];
}
return sum;
}
};
int main() {
int matrix[5][5] = {
{3, 0, 1, 4, 2} ,
{5, 6, 3, 2, 1},
{1, 2, 0, 1, 5},
{4, 1, 0, 1, 7},
{1, 0, 3, 0, 5}
};
vector<vector<int>> v(5,vector<int>(5));
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
v[i][j] = matrix[i][j];
}
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cout << v[i][j] << " ";
}
cout << endl;
}
NumMatrix numArray(v);
cout << numArray.sumRegion(2, 1, 4, 3) << endl;
cout << numArray.sumRegion(1, 1, 2, 2) << endl;
cout << numArray.sumRegion(1, 2, 2, 4) << endl;
system("pause");
return 0;
}
运行结果
解题思路二
二维前缀和,用preSum[i][j]表示:左上角为matrix[0][0],右下角为matrix[i][j]的矩形的所有元素之和,我们把这个区域分成四个部分,如图中的色块。假设计算preSum[i][j]时已经知道了perSum[i-1][j],perSum[i][j-1],perSum[i-1][j-1]的值,那么计算preSum[i][j]就变得很简单了!
此时preSum[i][j]的计算公式如下
p
r
e
S
u
m
[
i
]
[
j
]
=
p
r
e
S
u
m
[
i
−
1
]
[
j
]
+
p
r
e
S
u
m
[
i
]
[
j
−
1
]
−
p
r
e
S
u
m
[
i
−
1
]
[
j
−
1
]
+
m
a
t
r
i
x
[
i
]
[
j
]
preSum[i][j]=preSum[i-1][j]+preSum[i][j-1]-preSum[i-1][j-1]+matrix[i][j]
preSum[i][j]=preSum[i−1][j]+preSum[i][j−1]−preSum[i−1][j−1]+matrix[i][j]
为什么要减去preSum[i-1][j-1]呢?因为preSum[i-1][j]和preSum[i][j-1]都包含了preSum[i-1][j-1],即这个蓝色区域的和计算了两次,所以要减去一个preSum[i-1][j-1],最后还要单独加上红色区域,单个元素matrix[i][j]的值!
根据preSum数组求子矩形的区域和,假设需要求(row1,col1)到(row2,col2)的区域和,即粉红色区域的和。
用公式
s
u
m
R
e
g
i
o
n
(
r
o
w
1
,
c
o
l
1
,
r
o
w
2
,
c
o
l
2
)
=
p
r
e
S
u
m
[
r
o
w
2
]
[
c
o
l
2
]
−
p
r
e
S
u
m
[
r
o
w
2
]
[
c
o
l
1
−
1
]
−
p
r
e
S
u
m
[
r
o
w
1
−
1
]
[
c
o
l
2
]
+
p
r
e
S
u
m
[
r
o
w
1
−
1
]
[
c
o
l
1
−
1
]
sumRegion(row 1,col1,row2,col2) =preSum[row2][col2] − preSum[row2][col1−1] − preSum[row1−1][col2] + preSum[row1−1][col1−1]
sumRegion(row1,col1,row2,col2)=preSum[row2][col2]−preSum[row2][col1−1]−preSum[row1−1][col2]+preSum[row1−1][col1−1]
可以轻松算出粉红色区域的和!
举例:根据题目要求,需要计算如下黄色区间的和
根据公式
s
u
m
R
e
g
i
o
n
(
r
o
w
1
,
c
o
l
1
,
r
o
w
2
,
c
o
l
2
)
=
p
r
e
S
u
m
[
r
o
w
2
]
[
c
o
l
2
]
−
p
r
e
S
u
m
[
r
o
w
2
]
[
c
o
l
1
−
1
]
−
p
r
e
S
u
m
[
r
o
w
1
−
1
]
[
c
o
l
2
]
+
p
r
e
S
u
m
[
r
o
w
1
−
1
]
[
c
o
l
1
−
1
]
sumRegion(row 1,col1,row2,col2) =preSum[row2][col2] − preSum[row2][col1−1] − preSum[row1−1][col2] + preSum[row1−1][col1−1]
sumRegion(row1,col1,row2,col2)=preSum[row2][col2]−preSum[row2][col1−1]−preSum[row1−1][col2]+preSum[row1−1][col1−1]
可以算出黄色区域的和,即根据图中圈住的值进行计算!(代码实现的时候,使用的 preSum 比原矩阵 matrix 多了一行一列,是为了方便计算sumRegion(row1, col1, row2, col2),不需要对row1=0和col1=0的情况进行特殊处理)
最终使用代码实现的公式变为:
s
u
m
R
e
g
i
o
n
(
r
o
w
1
,
c
o
l
1
,
r
o
w
2
,
c
o
l
2
)
=
p
r
e
S
u
m
[
r
o
w
2
+
1
]
[
c
o
l
2
+
1
]
−
p
r
e
S
u
m
[
r
o
w
2
+
1
]
[
c
o
l
1
]
−
p
r
e
S
u
m
[
r
o
w
1
]
[
c
o
l
2
+
1
]
+
p
r
e
S
u
m
[
r
o
w
1
]
[
c
o
l
1
]
sumRegion(row 1,col1,row2,col2) =preSum[row2+1][col2+1] − preSum[row2+1][col1] − preSum[row1][col2+1] + preSum[row1][col1]
sumRegion(row1,col1,row2,col2)=preSum[row2+1][col2+1]−preSum[row2+1][col1]−preSum[row1][col2+1]+preSum[row1][col1]
代码
#include<iostream>
#include<vector>
using namespace std;
class NumMatrix {
public:
vector<vector<int>> preSum;
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)); //创建m+1行,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) {
return preSum[row2 + 1][col2 + 1] - preSum[row1][col2 + 1] - preSum[row2 + 1][col1] + preSum[row1][col1];
}
};
int main() {
int matrix[5][5] = {
{3, 0, 1, 4, 2} ,
{5, 6, 3, 2, 1},
{1, 2, 0, 1, 5},
{4, 1, 0, 1, 7},
{1, 0, 3, 0, 5}
};
vector<vector<int>> v(5,vector<int>(5));
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
v[i][j] = matrix[i][j];
}
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
cout << v[i][j] << " ";
}
cout << endl;
}
NumMatrix numArray(v);
cout << numArray.sumRegion(2, 1, 4, 3) << endl;
cout << numArray.sumRegion(1, 1, 2, 2) << endl;
cout << numArray.sumRegion(1, 2, 2, 4) << endl;
system("pause");
return 0;
}
运行结果:
参考资料:
https://leetcode-cn.com/problems/range-sum-query-2d-immutable/solution/er-wei-qu-yu-he-jian-suo-ju-zhen-bu-ke-b-2z5n/
https://leetcode-cn.com/problems/range-sum-query-2d-immutable/solution/ru-he-qiu-er-wei-de-qian-zhui-he-yi-ji-y-6c21/
https://leetcode-cn.com/problems/range-sum-query-2d-immutable/solution/er-wei-qian-zhui-he-jian-dan-tui-dao-tu-sqekv/