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

有什么不懂的地方欢迎留言!

题目描述

给定一个二维矩阵,计算其子矩形范围内元素的总和,该子矩阵的左上角为 (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[i1];

              此时当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[i1];
    (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[i1][j]+preSum[i][j1]preSum[i1][j1]+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][col11]preSum[row11][col2]+preSum[row11][col11]
可以轻松算出粉红色区域的和!

在这里插入图片描述

举例:根据题目要求,需要计算如下黄色区间的和
在这里插入图片描述
在这里插入图片描述
根据公式
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][col11]preSum[row11][col2]+preSum[row11][col11]
可以算出黄色区域的和,即根据图中圈住的值进行计算!(代码实现的时候,使用的 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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值