• 308. 二维区域和检索 - 可变 线段树/哈希


    308. 二维区域和检索 - 可变

    给你一个二维矩阵 matrix ,你需要处理下面两种类型的若干次查询:

    1. 更新:更新 matrix 中某个单元的值。
    2. 求和:计算矩阵 matrix 中某一矩形区域元素的  ,该区域由 左上角 (row1, col1) 和 右下角 (row2, col2) 界定。

    实现 NumMatrix 类:

    • NumMatrix(int[][] matrix) 用整数矩阵 matrix 初始化对象。
    • void update(int row, int col, int val) 更新 matrix[row][col] 的值到 val 。
    • int sumRegion(int row1, int col1, int row2, int col2) 返回矩阵 matrix 中指定矩形区域元素的  ,该区域由 左上角 (row1, col1) 和 右下角 (row2, col2) 界定。

    示例 1:

    输入
    ["NumMatrix", "sumRegion", "update", "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], [3, 2, 2], [2, 1, 4, 3]]
    输出
    [null, 8, null, 10]
    
    解释
    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); // 返回 8 (即, 左侧红色矩形的和)
    numMatrix.update(3, 2, 2);       // 矩阵从左图变为右图
    numMatrix.sumRegion(2, 1, 4, 3); // 返回 10 (即,右侧红色矩形的和)
    

    提示:

    • m == matrix.length
    • n == matrix[i].length
    • 1 <= m, n <= 200
    • -10^5 <= matrix[i][j] <= 10^5
    • 0 <= row < m
    • 0 <= col < n
    • -10^5 <= val <= 10^5
    • 0 <= row1 <= row2 < m
    • 0 <= col1 <= col2 < n
    • 最多调用10^4 次 sumRegion 和 update 方法

    来源:力扣(LeetCode)
    链接:https://leetcode.cn/problems/range-sum-query-2d-mutable
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    做题结果

    成功,用了两种解法

    方法1:哈希

    懒加载的二维前缀和,好处是一直update或一直求和,复杂度还是O(1),交替时是 O(mn)

    1. 初始化前缀和

    2. 更新数据时,标记

    3.  获取有标记时,更新数组,去除标记

    1. class NumMatrix {
    2. int[][] arr;
    3. int[][] pre;
    4. int m;
    5. int n;
    6. boolean hasChange;
    7. public NumMatrix(int[][] matrix) {
    8. arr = matrix;//改前缀和。。。
    9. m = matrix.length;
    10. n = matrix[0].length;
    11. pre = new int[m+1][n+1];
    12. for(int i = 0; i<m; i++){
    13. for(int j = 0; j < n; j++){
    14. pre[i+1][j+1]=matrix[i][j]+pre[i][j+1]+pre[i+1][j]-pre[i][j];
    15. }
    16. }
    17. }
    18. public void update(int row, int col, int val) {
    19. arr[row][col] = val;
    20. hasChange = true;
    21. }
    22. public int sumRegion(int row1, int col1, int row2, int col2) {
    23. if(hasChange){
    24. for(int i = 0; i<m; i++){
    25. for(int j = 0; j < n; j++){
    26. pre[i+1][j+1]=arr[i][j]+pre[i][j+1]+pre[i+1][j]-pre[i][j];
    27. }
    28. }
    29. hasChange = false;
    30. }
    31. return pre[row2+1][col2+1]+pre[row1][col1]-pre[row1][col2+1]-pre[row2+1][col1];
    32. }
    33. }

    方法2:线段树 

    这题其实是二维线段树,记录左上角和右下角,共四个点

    1. 建立线段树,共四个点,一个和,两个孩子

    2. 递归分配孩子时,优先分行,再分列【方便,一次分四个也可以,需要判断的会多一点】

    3. 算出孩子的值,逐一往上递推,拿到整体前缀和

    4. 添加元素:不在范围内返回,在范围内递推添加,传递到孩子

    5. 求和: 当前范围值,都在目标范围内直接返回sum;都不在目标内直接返回0;否则累计孩子的和

    1. class NumMatrix {
    2. SquareTree tree;
    3. int[][] arr;
    4. public NumMatrix(int[][] matrix) {
    5. arr = matrix;
    6. tree = new SquareTree(0,0,matrix.length-1,matrix[0].length-1,matrix);
    7. }
    8. public void update(int row, int col, int val) {
    9. int diff = val-arr[row][col];
    10. arr[row][col] = val;
    11. tree.add(row,col,diff);
    12. }
    13. public int sumRegion(int row1, int col1, int row2, int col2) {
    14. return tree.getSum(row1,col1,row2,col2);
    15. }
    16. }
    17. class SquareTree{
    18. int x1;
    19. int x2;
    20. int y1;
    21. int y2;
    22. int sum;
    23. SquareTree t1;
    24. SquareTree t2;
    25. public SquareTree(int x1, int y1, int x2, int y2,int[][] arr){
    26. this.x1 = x1;
    27. this.y1 = y1;
    28. this.x2 = x2;
    29. this.y2 = y2;
    30. sum = initSum(x1,y1,x2,y2,arr);
    31. }
    32. private int initSum(int X1, int Y1, int X2, int Y2, int[][] arr){
    33. if(X1==X2 && Y1 == Y2){
    34. sum = arr[X1][Y1];
    35. return sum;
    36. }
    37. if(x1<x2){
    38. int mid = (x2-x1)/2+x1;
    39. t1 = new SquareTree(x1,y1,mid,y2,arr);
    40. t2 = new SquareTree(mid+1,y1,x2,y2,arr);
    41. }else{
    42. int mid = (y2-y1)/2+y1;
    43. t1 = new SquareTree(x1,y1,x2,mid,arr);
    44. t2 = new SquareTree(x1,mid+1,x2,y2,arr);
    45. }
    46. sum = t1.sum+t2.sum;
    47. return sum;
    48. }
    49. public void add(int x, int y,int v){
    50. if(x<x1||x>x2||y<y1||y>y2){
    51. return;
    52. }
    53. sum += v;
    54. if(t1!=null)
    55. t1.add(x,y,v);
    56. if(t2!=null)
    57. t2.add(x,y,v);
    58. }
    59. public int getSum(int X1, int Y1, int X2, int Y2){
    60. if(x2<X1||x1>X2||y2<Y1||y1>Y2){
    61. return 0;
    62. }
    63. //都在范围里
    64. if(X1<=x1&&X2>=x2&&Y1<=y1&&Y2>=y2){
    65. return sum;
    66. }
    67. return t1.getSum(X1,Y1,X2,Y2)+t2.getSum(X1,Y1,X2,Y2);
    68. }
    69. }

  • 相关阅读:
    Android Compose 代码示例 pull Refresh
    面试必知的9个性能测试指标,你完全了解吗?
    一起Talk Android吧(第三百七十二回:Timer的陷阱)
    【ASM】字节码操作 Label 如何使用 生成 if 语句
    Java变量类型 Java进阶必看
    AVR汇编(二):AVR架构介绍
    SSM+Java体育用品库存管理系统 毕业设计-附源码211712
    ChatGPT:怎么用Java调出来文件选择器,然后返回文件的位置和名称?Swing 组件和 AWT 组件:Java GUI 编程的不同之处
    四向车立体库|四向穿梭车AGV如何进行入库和出库?
    软件测试/测试开发丨ChatGPT在测试计划中的应用策略
  • 原文地址:https://blog.csdn.net/yu_duan_hun/article/details/125488424