2021/03/02 每日一题 二维区域和检索 - 矩阵不可变

LeetCode上二维区域和检索 - 矩阵不可变,与昨天相比,是将数组变为二维数组来求范围内的和

同样可以用暴力求法来遍历范围内的所有数相加得到结果,和昨天的题目一样,这样做能够通过提交但是时间和内存消耗都比较大

var NumMatrix = function(matrix) {
  this.matrix = matrix
};
NumMatrix.prototype.sumRegion = function(row1, col1, row2, col2) {
  // 暴力循环
  let res = 0
  for(let i = row1; i <= row2; i++) {
    for(let j = col1;j <= col2;j++) {
      res += this.matrix[i][j]
    }
  }
  return res
};

所以还是会通过前缀和来优化程序,这里使用了二维数组下的前缀和
通过2021/03/01 每日一题 区域和检索 - 数组不可变这篇文章,知道了前缀和是前i项数组的和即presum[i] = nums[i] + nums[i -1] + ....+ nums[0],那么怎么用这个前缀和对应到二维数组上?
求取前缀和数组
假设传入一个二维数组matrix = [[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]]排列成表格如下图所示


假设现在求[0,0]到[3,3]范围区间的和

可以将[0,0]到[3,3]范围分为以下几块区域
presum[2][3]

presum[3][2]

presum[2][2]

matrix[3][3]

把上面的几个区域拼接就能得到presum[3][3]
presum[3][3] = presum[2][3] + presum[3][2] - presum[2][2] + matrix[3][3]
因为中间presum[2][2]区域计算过了2次,所以最后要减掉一次来保证数据的准确
最后推导的公式为presum[i][j] = presum[i-1][j] + presum[i][j-1] - presum[i-1][j-1] + matrix[i][j],并且和之前的一维数组一样,多添加一行,用于计算i=0,j=0情况下的前缀和,那么就可以获得到下面的二维数组


通过查表就能快速计算结果,例如现在要计算中间任意[i,j]位置的数都可以通过上面公式来计算
例如计算[4,4]
presum[4,4] = presum[3][4] + presum[4][3] - presum[3][3] + matrix[3][3] = 28 + 26 - 21 + 1 = 34
根据表格验算确实为此值,同时因为整体扩展了一行,所以对应的presum[4,4],就是matrix[0][0] - matrix[3][3]之间所有数的和

求取结果
如果计算matrix[1][1] - matrix[3][3]之间所有数的和通过我们前面求取的二维数组前缀和又要怎么计算?


求取左边数组绿色区域的数之和因为前缀和的计算已经知道了presum[4][4]的值(这里4是因为都增加了1行1列),即图中绿色+蓝色区域所有数的和

之后只需要用presum[4][4] - presum[1][4] - presum[4][1] + presum[1][1],多加一次presum[1][1]是因为多减掉了一次,需要加回来,那么查看右边数组可以得到
rangeSum(1,1,3,3) = presum[4][4] - presum[1][4] - presum[4][1] + presum[1][1] = 34 - 8 - 13 + 3 = 16
验算可得结果正确,所以最后能够推导的公式是
rangeSum(r1,c1,r2,c2) = presum[r2+1][c2+1] - presum[r1][c2+1] - presum[r2 + 1][c1] + presum[r1][c1]

所以整个过程可以表示如下:

  1. 在创建类的时候使用
    presum[i][j] = presum[i-1][j] + presum[i][j-1] - presum[i-1][j-1] + matrix[i][j]
    计算出前缀和数组
  2. 在求范围和的时候使用
    rangeSum(r1,c1,r2,c2) = presum[r2+1][c2+1] - presum[r1][c2+1] - presum[r2 + 1][c1] + presum[r1][c1]
    通过前缀和数组来计算求和

最终代码如下

var NumMatrix = function(matrix) {
    let rows, cols
    if (!matrix.length) {
        rows = cols = 1
    } else {
        // 要扩展一行一列
        rows = matrix.length + 1
        cols = matrix[0].length + 1
    }
    // 快速定义二维数组,并且填充0
    let preSum = Array.from({ length: rows }, () => new Array(cols).fill(0))
    // 计算前缀和,从1开始跳过扩展的地方
    for (i = 1; i < rows; i++) {
        for (j = 1; j < cols; j++) {
            preSum[i][j] =preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + matrix[i - 1][j - 1]
        }
    }
    this.preSum = preSum
};

NumMatrix.prototype.sumRegion = function(row1, col1, row2, col2) {
    let preSum = this.preSum
    return preSum[row2 + 1][col2 + 1] - preSum[row1][col2 + 1] - preSum[row2 + 1][col1] + preSum[row1][col1]
};


可以发现用时下降了50%+,内存消耗也减少了

总结下前缀和

  1. 前缀和可以减少重复计算,因为不会每次都遍历求和
  2. 生成前缀和数组的时候还是需要计算的
  3. 如果只计算少量次数前缀和看不出太多优势,优势在于多次计算的时候,只需要查表就能求值,一次求前缀和数组的计算,后面就能多次使用,从而减少时间消耗
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352

推荐阅读更多精彩内容