leetcode
leetcode 2201 ~ 2250
子矩阵元素加 1

子矩阵元素加 1

难度:

标签:

题目描述

You are given a positive integer n, indicating that we initially have an n x n 0-indexed integer matrix mat filled with zeroes.

You are also given a 2D integer array query. For each query[i] = [row1i, col1i, row2i, col2i], you should do the following operation:

  • Add 1 to every element in the submatrix with the top left corner (row1i, col1i) and the bottom right corner (row2i, col2i). That is, add 1 to mat[x][y] for all row1i <= x <= row2i and col1i <= y <= col2i.

Return the matrix mat after performing every query.

 

Example 1:

Input: n = 3, queries = [[1,1,2,2],[0,0,1,1]]
Output: [[1,1,0],[1,2,1],[0,1,1]]
Explanation: The diagram above shows the initial matrix, the matrix after the first query, and the matrix after the second query.
- In the first query, we add 1 to every element in the submatrix with the top left corner (1, 1) and bottom right corner (2, 2).
- In the second query, we add 1 to every element in the submatrix with the top left corner (0, 0) and bottom right corner (1, 1).

Example 2:

Input: n = 2, queries = [[0,0,1,1]]
Output: [[1,1],[1,1]]
Explanation: The diagram above shows the initial matrix and the matrix after the first query.
- In the first query we add 1 to every element in the matrix.

 

Constraints:

  • 1 <= n <= 500
  • 1 <= queries.length <= 104
  • 0 <= row1i <= row2i < n
  • 0 <= col1i <= col2i < n

代码结果

运行时间: 213 ms, 内存: 35.7 MB


/*
 * 思路:
 * 1. 初始化一个 n x n 的矩阵 mat,全部元素设为 0。
 * 2. 使用 Java Stream 对每一个 query 进行处理。
 * 3. 对于每一个 query,通过 stream 遍历其指定的子矩阵区域,将该区域内的每个元素加 1。
 * 4. 返回最终的矩阵 mat。
 */

import java.util.Arrays;
import java.util.stream.IntStream;

public class Solution {
    public int[][] rangeAddQueries(int n, int[][] queries) {
        int[][] mat = new int[n][n];
        Arrays.stream(queries).forEach(query -> {
            int row1 = query[0];
            int col1 = query[1];
            int row2 = query[2];
            int col2 = query[3];
            IntStream.rangeClosed(row1, row2).forEach(i -> 
                IntStream.rangeClosed(col1, col2).forEach(j -> mat[i][j]++)
            );
        });
        return mat;
    }
}

解释

方法:

此题解使用了二维差分数组的方法来高效处理范围加法问题。首先,创建一个 (n+2)x(n+2) 的差分数组 diff,初始化所有值为0。对于每个查询,使用差分数组的性质,通过更新矩阵的四个关键点来表示子矩阵内所有元素增加1的操作。这四个点是:左上角、右上角+1、左下角+1和右下角+1+1。最后,通过计算二维前缀和来还原出最终的矩阵,这样避免了直接对每个子矩阵内的每个元素进行迭代更新,从而提高效率。

时间复杂度:

O(n^2)

空间复杂度:

O(n^2)

代码细节讲解

🦆
在二维差分数组中,为什么选择使用(n+2)x(n+2)的数组大小而不是直接使用n x n的尺寸?
在使用二维差分数组处理范围增加操作时,选择使用(n+2)x(n+2)的数组大小主要是为了方便处理边界情况。当我们需要在子矩阵的右侧或下方增加边界值时,使用n+2的尺寸可以确保即使是在矩阵的最右侧或最下侧的边界也能正确处理。例如,当操作需要在最右列或最下行添加值时,不会越界。这样就避免了额外的边界检查,简化了代码实现。
🦆
如何确保在对查询处理时,更新差分数组的四个关键点后不会影响到数组的其他部分?
在二维差分数组中,通过更新四个关键点来表示子矩阵内所有元素增加1的操作。这四个点分别是左上角、右上角右侧一列、左下角下方一行、以及右下角右侧一列和下方一行。这种更新方式是局部的,只影响子矩阵的边界,因此不会影响到数组的其他部分。每次更新是针对特定区域的,且通过前缀和的计算,这些局部的更新最终会累积到对应的子矩阵内,其他区域不受影响。
🦆
在计算二维前缀和的过程中,为什么要使用`diff[i][j] += diff[i][j - 1] + diff[i - 1][j] - diff[i - 1][j - 1]`这个公式?
这个公式是二维前缀和的核心,用于计算从矩阵起始点到当前点(i, j)包含的所有值的累积和。这里`diff[i][j - 1]`和`diff[i - 1][j]`分别代表左边和上边的累积前缀和,而`diff[i - 1][j - 1]`则被加了两次(一次在左边累积,一次在上面累积),因此需要减去一次以消除重复计数。这样,每个元素的值都是基于其左上角所有元素的累积效果,确保了正确计算每个点的实际值。
🦆
如何处理当查询的坐标超出矩阵初始边界时的情况,在代码中有何体现?
在本题的代码实现中,由于使用了(n+2)x(n+2)的差分数组,实际上已经隐式地处理了查询坐标可能超出矩阵初始边界的情况。即使查询的坐标超出了原始矩阵的边界,由于差分数组的额外边界,这些查询不会引起数组越界错误。此外,代码中没有特别处理这种情况,因为假设查询总是有效的,即在矩阵的合理范围内。如果需要明确处理超出边界的查询,可以在代码中添加条件语句来限制或调整超出边界的坐标值。

相关问题