LeetCode 2536. 子矩阵元素加 1

2536. 子矩阵元素加 1

给你一个正整数 n ,表示最初有一个 n x n 、下标从 0 开始的整数矩阵 mat ,矩阵中填满了 0 。

另给你一个二维整数数组 query 。针对每个查询 query[i] = [row1i, col1i, row2i, col2i] ,请你执行下述操作:

  • 找出 左上角 为 (row1i, col1i) 且 右下角 为 (row2i, col2i) 的子矩阵,将子矩阵中的 每个元素 加 1 。也就是给所有满足 row1i <= x <= row2i 和 col1i <= y <= col2i 的 mat[x][y] 加 1 。

返回执行完所有操作后得到的矩阵 mat 。

示例 1:

输入:n = 3, queries = [[1,1,2,2],[0,0,1,1]]
输出:[[1,1,0],[1,2,1],[0,1,1]]
解释:上图所展示的分别是:初始矩阵、执行完第一个操作后的矩阵、执行完第二个操作后的矩阵。
- 第一个操作:将左上角为 (1, 1) 且右下角为 (2, 2) 的子矩阵中的每个元素加 1 。 
- 第二个操作:将左上角为 (0, 0) 且右下角为 (1, 1) 的子矩阵中的每个元素加 1 。 

示例 2:

输入:n = 2, queries = [[0,0,1,1]]
输出:[[1,1],[1,1]]
解释:上图所展示的分别是:初始矩阵、执行完第一个操作后的矩阵。 
- 第一个操作:将矩阵中的每个元素加 1 。

提示:

  • 1 <= n <= 500
  • 1 <= queries.length <= 10^4
  • 0 <= row1i <= row2i < n
  • 0 <= col1i <= col2i < n

提示 1

Imagine each row as a separate array. Instead of updating the whole submatrix together, we can use prefix sum to update each row separately.


提示 2

For each query, iterate over the rows i in the range [row1, row2] and add 1 to prefix sum S[i][col1], and subtract 1 from S[i][col2 + 1].


提示 3

After doing this operation for all the queries, update each row separately with S[i][j] = S[i][j] + S[i][j - 1].

解法:二维差分 + 二维前缀和

回想下,一维差分是怎么做的。

把一段区间的元素 +1,可以记录「变化量」,把区间起点 +1,区间终点右侧 −1,这样「变化量的前缀和」就是实际结果。注意 −1 的地方是终点下标 +1 的位置。

推广到二维,也就是把一个区域的元素都 +1,那就需要用一个二维数组来记录变化量,然后对这个二维数组求二维前缀和,就得到了实际结果,即本题的 mat。

怎么记录二维的变化量呢?

从二维前缀和的角度来看,对区域左上角 +1 会对所有右下位置产生影响,那么在区域右上角的右边相邻处和左下角的下边相邻处 −1 可以消除这个影响,但是两个 −1 又会对区域右下角的右下所有位置产生影响,所以要在右下角的右下相邻处再 +1 还原回来。

Java版:

class Solution {
    public int[][] rangeAddQueries(int n, int[][] queries) {
        int[][] diff = new int[n + 2][n + 2];
        // 二维差分
        for (int[] q: queries) {
            int r1 = q[0];
            int c1 = q[1];
            int r2 = q[2];
            int c2 = q[3];
            diff[r1 + 1][c1 + 1] += 1;
            diff[r2 + 2][c1 + 1] -= 1;
            diff[r1 + 1][c2 + 2] -= 1;
            diff[r2 + 2][c2 + 2] += 1;
        }

        int[][] ans = new int[n][n];
        // 用二维前缀和复原
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                ans[i - 1][j - 1] = diff[i][j] += diff[i][j - 1] + diff[i - 1][j] - diff[i - 1][j - 1];
            }
        }
        return ans;
    }
}

Python3版:

class Solution:
    def rangeAddQueries(self, n: int, queries: List[List[int]]) -> List[List[int]]:
        diff = [[0] * (n + 2) for _ in range(n + 2)]
        # 二维差分
        for r1, c1, r2, c2 in queries:
            diff[r1 + 1][c1 + 1] += 1
            diff[r1 + 1][c2 + 2] -= 1
            diff[r2 + 2][c1 + 1] -= 1
            diff[r2 + 2][c2 + 2] += 1
        
        # 用二维前缀和复原(原地修改)
        for i in range(1, n + 1):
            for j in range(1, n + 1):
                diff[i][j] += diff[i][j - 1] + diff[i - 1][j] - diff[i - 1][j - 1]
        
        # 保留中间 n*n 的部分,即为答案
        diff = diff[1: -1]
        for i, row in enumerate(diff):
            diff[i] = row[1: -1]
        
        return diff
复杂度分析
  • 时间复杂度:O(n^2+q),其中 q 为 queries 的长度。
  • 空间复杂度:O(n^2)。
  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值