【中等】力扣算法题解析LeetCode308:二维区域和检索-矩阵可修改

关注文末推广名片,即可免费获得本题测试源码

题目来源:🔒308:二维区域和检索-矩阵可修改

问题抽象: 设计一个类 NumMatrix,用于高效处理 动态二维整数矩阵的任意子矩阵和查询与单点修改,满足以下核心需求:

  1. 类功能定义

    • 初始化:接收二维整数矩阵 matrix 并构建支持动态操作的数据结构;
    • 更新方法update(int row, int col, int val)matrix[row][col] 的值修改为 val
    • 查询方法sumRegion(int row1, int col1, int row2, int col2) 返回以 (row1, col1) 为左上角、(row2, col2) 为右下角的矩形区域元素和(包含边界)。
  2. 操作约束

    • 初始化效率:构造函数时间复杂度 O(mn)(允许预处理,m×n 为矩阵尺寸);
    • 更新高效update 方法时间复杂度 O(log m × log n)(避免重构全矩阵);
    • 查询高效sumRegion 方法时间复杂度 O(log m × log n)(避免扫描子矩阵);
    • 空间复杂度 O(mn):额外存储结构尺寸与输入矩阵相当。
  3. 输入输出

    • 输入矩阵 matrix:尺寸 m×n1 ≤ m, n ≤ 200),元素值 -10^5 ≤ matrix[i][j] ≤ 10^5
    • 更新参数:0 ≤ row < m, 0 ≤ col < n,新值 val(整数);
    • 查询参数:0 ≤ row1 ≤ row2 < m, 0 ≤ col1 ≤ col2 < n
    • 输出:区域和(整数)。
  4. 边界处理

    • 空矩阵m=0n=0 时,更新/查询操作仍可执行(返回 0 或无操作);
    • 单点更新后查询:如 update(0,0,5)sumRegion(0,0,0,0) 返回 5
    • 大区域查询:如 sumRegion(0,0,m-1,n-1) 返回矩阵总和;
    • 高频操作:支持 10^4 次更新和查询操作(总调用次数上限)。

类接口定义

class NumMatrix {  
    public NumMatrix(int[][] matrix);  // 初始化  
    public void update(int row, int col, int val);  // 单点修改  
    public int sumRegion(int row1, int col1, int row2, int col2);  // 区域和查询  
}  

应用场景:实时更新的网格数据统计(如动态图表、游戏地图状态监控)。


解题思路

核心问题:在动态更新的二维矩阵中高效计算任意矩形区域的和,需同时优化 updatesumRegion 操作的时间复杂度。

算法选型:二维树状数组(Binary Indexed Tree)

  • 为什么选择树状数组?
    传统二维前缀和在更新时需要 O(mn) 时间重建,不适用于频繁更新场景。树状数组支持:

    • 单点更新:时间复杂度 O(log m * log n)
    • 区间求和:时间复杂度 O(log m * log n)
      适合本题更新和查询均匀调用的场景。
  • 二维树状数组原理

    1. 数据结构:维护一个二维数组 tree,其中 tree[i][j] 存储从 (i - lowbit(i) + 1, j - lowbit(j) + 1)(i, j) 的矩形区域和(lowbit(x) = x & -x)。
    2. 更新操作:当修改位置 (row, col) 的值时,需更新所有包含该位置的树状数组区域。通过迭代 i = row + 1N(每次 i += lowbit(i)),并在每个 i 中迭代 j = col + 1M(每次 j += lowbit(j)),更新 tree[i][j]
    3. 求和操作:计算 (0, 0)(row, col) 的二维前缀和时,逆向迭代(i = row + 11,每次 i -= lowbit(i)j = col + 11,每次 j -= lowbit(j)),累加 tree[i][j]
    4. 区域和计算:利用二维前缀和的容斥原理:
      sumRegion(row1, col1, row2, col2) = 
          sum(row2, col2) 
          - sum(row1 - 1, col2) 
          - sum(row2, col1 - 1) 
          + sum(row1 - 1, col1 - 1)
      

时间复杂度: 初始化O(mn * log m * log n),每个元素需更新树状数组。updateO(log m * log n)sumRegionO(log m * log n)


代码实现(Java版)🔥点击下载源码

class NumMatrix {
    private int[][] tree;   // 二维树状数组
    private int[][] nums;   // 原始矩阵
    private int N;          // 矩阵行数
    private int M;          // 矩阵列数

    public NumMatrix(int[][] matrix) {
        if (matrix.length == 0 || matrix[0].length == 0) return;
        N = matrix.length;
        M = matrix[0].length;
        nums = new int[N][M];
        tree = new int[N + 1][M + 1];  // 树状数组下标从1开始
        
        // 初始化:逐个更新元素到树状数组
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < M; j++) {
                update(i, j, matrix[i][j]);
            }
        }
    }
    
    // 更新操作:将 matrix[row][col] 设为 val
    public void update(int row, int col, int val) {
        if (N == 0 || M == 0) return;
        int delta = val - nums[row][col]; // 计算增量
        nums[row][col] = val;             // 更新原始矩阵
        
        // 更新所有包含 (row, col) 的树状数组区域
        for (int i = row + 1; i <= N; i += lowbit(i)) {
            for (int j = col + 1; j <= M; j += lowbit(j)) {
                tree[i][j] += delta;
            }
        }
    }
    
    // 计算 (0,0) 到 (row, col) 的二维前缀和
    public int sum(int row, int col) {
        int total = 0;
        // 逆向迭代:从 (row+1, col+1) 向左上累加
        for (int i = row + 1; i > 0; i -= lowbit(i)) {
            for (int j = col + 1; j > 0; j -= lowbit(j)) {
                total += tree[i][j];
            }
        }
        return total;
    }
    
    // 计算区域和:利用二维前缀和容斥
    public int sumRegion(int row1, int col1, int row2, int col2) {
        if (N == 0 || M == 0) return 0;
        return sum(row2, col2) 
             - sum(row1 - 1, col2) 
             - sum(row2, col1 - 1) 
             + sum(row1 - 1, col1 - 1);
    }
    
    // 辅助函数:计算 x 的二进制最低位1的值
    private int lowbit(int x) {
        return x & (-x);
    }
}

代码说明

  1. 树状数组下标处理

    • 树状数组下标从 1 开始,因此实际使用时需将行列 +1(如 row + 1)。
    • lowbit(x) 函数用于快速计算二进制最低位 1 的值(如 lowbit(6) = 2)。
  2. 更新操作 (update)

    • 计算新值与旧值的增量 delta
    • 通过双重循环更新所有覆盖 (row, col) 的树状数组节点。外层循环沿行更新(i += lowbit(i)),内层循环沿列更新(j += lowbit(j))。
  3. 求和操作 (sumRegion)

    • 先计算 (0, 0)(row, col) 的二维前缀和 sum(row, col),通过逆向迭代(i -= lowbit(i), j -= lowbit(j))累加树状数组的值。
    • 区域和通过四个前缀和的组合计算:
      sumRegion = sum(B) - sum(A) - sum(C) + sum(D)
      
      其中 B=(row2,col2), A=(row1-1,col2), C=(row2,col1-1), D=(row1-1,col1-1)
  4. 边界处理

    • row1 = 0col1 = 0,则 sum(row1 - 1, ...) 自动返回 0(因循环条件 i > 0)。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达文汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值