一. 数组_前缀和数组_304. 二维区域和检索 - 矩阵不可变

题目描述
给定一个二维矩阵 matrix,以下类型的多个请求:

计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。
实现 NumMatrix 类:

NumMatrix(int[][] matrix) 给定整数矩阵 matrix 进行初始化
int sumRegion(int row1, int col1, int row2, int col2) 返回 左上角 (row1, col1) 、右下角 (row2, col2) 所描述的子矩阵的元素 总和 。
示例 1:
在这里插入图片描述
输入:
[“NumMatrix”,“sumRegion”,“sumRegion”,“sumRegion”]
[[[[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]]],[2,1,4,3],[1,1,2,2],[1,2,2,4]]
输出:
[null, 8, 11, 12]

解释:
NumMatrix numMatrix = new NumMatrix([[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]]]);
numMatrix.sumRegion(2, 1, 4, 3); // return 8 (红色矩形框的元素总和)
numMatrix.sumRegion(1, 1, 2, 2); // return 11 (绿色矩形框的元素总和)
numMatrix.sumRegion(1, 2, 2, 4); // return 12 (蓝色矩形框的元素总和)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/range-sum-query-2d-immutable

解法1
这里首先使用最慢的暴力方法解决问题,也就是包含row1,col1和row2,col2的区域内元素的和
这里下标是从0开始的,这里相当于给定了行和列下标的范围row1row2,col1col2
在测试实例17的时候,没有通过,因为测试实例17的时候,实在是太长了,for循环也不可以,没有通过,但是这个代码是没有问题的
肯定是可以使用的,但是会超时

class NumMatrix {
    vector<vector<int>> matrix;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        this->matrix = matrix; //直接值传递
    }

    int sumRegion(int row1, int col1, int row2, int col2) {
        int sum = 0;

        //这里给定的下标应该都是范围内的,没有越界的情况,所以不需要检查,这里包含边界处的值
        for(int i = row1; i <= row2; i++) {
            for(int j = col1; j <= col2; j++) {
                sum += matrix[i][j];
            }
        }

        return sum;
    }
};

我们这里参考303一维前缀和的思路
一维数组前缀和
我们发现这里33的一个区域的话,我们可以看成是三行,这样的话,只需要col2处,减去col1处的值即可得到一行的和
这样三行的和也是可以得到的
所以我们需要将原始矩阵,按照求和的方式,不断累加,每行相当于一个一维数组,我们设置n
n+1大小的矩阵,每行存储前缀和,不同行之间没关系
1.首先初始化构造前缀和
2.取对应行的对应前缀和相减,这个结果在相加即可。
可以通过编译,内存消耗比例也还可以接受,但是时间太长了,运行时间特比长。
时间和空间复杂度是O(mn)

class NumMatrix {
    vector<vector<int>> sums;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        sums.resize(m);

        for(int i = 0; i < m; i++) {
            sums[i].resize(n + 1);

            //这里运行sums[0] = 0,而sums[1] = nums[0] + sums[0]
            //sums[2] = nums[1] +sums[1]   这里,当前值+前面那些的和
            for(int j = 1; j <= n; j++) {
                //当前值+前面的和
                sums[i][j] = matrix[i][j - 1] + sums[i][j - 1];
            }
        }
    }

    int sumRegion(int row1, int col1, int row2, int col2) {
        int sum = 0;

        //这里给定的下标应该都是范围内的,没有越界的情况,所以不需要检查,这里包含边界处的值
        //这里就是将矩形区域看成多个向量形式,将向量形式的数组进行求和,求和之后进行累加,得到下面的结果
        for(int i = row1; i <= row2; i++) {
            sum += (sums[i][col2 + 1] - sums[i][col1]);
        }

        return sum;
    }
};

二维数组前缀和
这里首先初始化矩阵的时候,构造二维前缀和,下面在使用的时候结果为(row2,col2)-(row1, col2) - (row2, col1) + (row1, col1)
二维前缀和的前提是一维前缀和,先构造一维前缀和,在从第二行开始,逐行加上一行的该元素即可。
1.首先构造二维前缀和
2.利用(row2, col2) - 两个边界 + 公共边界
类似于要求图中红色的区域,我们使用整个图减去蓝色区域,减去紫色区域,加上橙色区域,那么就等于红色区域。这里面的蓝色和紫色区域都包含这个橙色区域,所以这样就减掉了两个橙色区域,所以我们要加上一个橙色区域。
注:这里官方题解和我建立的sums矩阵,还有返回解的方式是相同的,不同的是,官方建立sums矩阵
的步骤和我的不同,表示的意思是一样的。
这里,官方给的题解为f(i, j) = f(i-1, j) + f(i, j-1) - f(i-1, j-1) + matrix[i][j]
在这里插入图片描述

class NumMatrix {
    vector<vector<int>> sums;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        //这里设置行数为m+1,列数n+1
        //这里行数和列数加1,是因为避免0的时候需要单独处理
        sums.resize(m + 1, vector<int>(n + 1)); //直接设置大小为m+1,n+1

        //此处构造矩阵,当原始矩阵为1*1的矩阵,那么也可以无差错的构造出合适的矩阵
        //这里构造一维的前缀和
        //第0行全为0,为了越界所考虑的
        //这里从第一行第一列开始计算sums的一维前缀和
        for(int i = 1; i <= m; i++) {
            //这里运行sums[0] = 0,而sums[1] = nums[0] + sums[0]
            //sums[2] = nums[1] +sums[1]   这里,当前值+前面那些的和
            for(int j = 1; j <= n; j++) {
                //当前值+前面的和
                sums[i][j] = matrix[i - 1][j - 1] + sums[i][j - 1];
            }
        }

        //这里通过一维前缀和,相加得到二维前缀和
        //这个前缀和没有任何问题
        //从第二行第一列开始计算二维前缀和
        for(int i = 2; i <= m; i++) {
            for(int j = 1; j <= n; j++) {
                sums[i][j] += sums[i - 1][j];
            }
        }
    }
    //此处矩阵的区域相减还有些问题,在只有一个元素的时候会报错
    int sumRegion(int row1, int col1, int row2, int col2) {
        //这里相当于最外面的大矩形减去两边小矩形在加上公共部分
        return sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row1][col1];
    }
};

这里创建初始化矩阵的步骤换一下,我们在这里
1.如果要计算(row, col)的二维前缀和的话,我们需要知道两个位置的值
2.(row, col) = (row-1, col) + (row, col-1) - (row-1, col-1) + (row, col),这里就需要左边元素和上边元素的值,和当前元素的值
就可以计算出来了,这样只要按照从上到下,从左到右的方式,遍历那么这些位置都是已知的。
3.初始化的时候,(1, 1) = (0,1) + (1,0) - (0,0) + (1,1),这样没有错误和问题,我们可以很轻松的得到结果
4.第一行和第一列都是0,这些都是为了不单独处理0而设置的。

class NumMatrix {
    vector<vector<int>> sums;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        //这里设置行数为m+1,列数n+1
        //这里行数和列数加1,是因为避免0的时候需要单独处理
        sums.resize(m + 1, vector<int>(n + 1)); //直接设置大小为m+1,n+1

        //这个成立的前提是他的上面和左边都是已经满足为0到该区域的目标和
        //从第一行开始,从左向右,这样就能满足这个条件。
        //这个其实和之前的那个先一行前缀和,在逐行累加的是一样的。
        for(int i = 1; i <= m; i++) {   //m+1行
            for(int j = 1; j <= n; j++) {   //n+1行
                sums[i][j] = sums[i - 1][j] + sums[i][j - 1] - sums[i - 1][j - 1] + matrix[i - 1][j - 1];
            }
        }
    }
    //此处矩阵的区域相减还有些问题,在只有一个元素的时候会报错
    int sumRegion(int row1, int col1, int row2, int col2) {
        //这里相当于最外面的大矩形减去两边小矩形在加上公共部分
        return sums[row2 + 1][col2 + 1] - sums[row1][col2 + 1] - sums[row2 + 1][col1] + sums[row1][col1];
    }
};
int main() {
    //["NumArray", "sumRange", "sumRange", "sumRange"] 输入
    //[[[-2, 0, 3, -5, 2, -1]], [0, 2], [2, 5], [0, 5]] 输入
    //[null, 1, -1, -3] 输出
    //这里第一个为null,这里因为第一个调用方法,所以返回值是null,其他的有返回值
    //这个的意思是方法对应的参数是什么,也就是给什么方法,传入什么参数
    vector<vector<int>> nums(2, vector<int>(2));    //这个是传入的参数
    nums[0][0] = 1;
    nums[0][1] = 1;
    nums[1][0] = 1;
    nums[1][1] = 1;
    NumMatrix *numArray = new NumMatrix(nums);
    cout << numArray->sumRegion(0, 0, 0, 0);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值