[leetcode] 307. Range Sum Query - Mutable

441 篇文章 0 订阅
284 篇文章 0 订阅

Description

Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.

The update(i, val) function modifies nums by updating the element at index i to val.

Example:

Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8

Note:

  1. The array is only modifiable by the update function.
  2. You may assume the number of calls to update and sumRange function is distributed evenly.

分析

题目的意思是:区间求和,然后可以更新某个位置上的数。

  • 这是一个树状数组Binary Indexed Tree的题目。
    所有的奇数位置的数字和原数组对应位置的相同,偶数位置是原数组若干位置之和,假如原数组A(a1, a2, a3, a4 …),和其对应的树状数组C(c1, c2, c3, c4 …)有如下关系:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
...

那么是如何确定某个位置到底是有几个数组成的呢,原来是根据坐标的最低位Low Bit来决定的,所谓的最低位,就是二进制数的最右边的一个1开始,加上后面的0(如果有的话)组成的数字,例如1到8的最低位如下面所示:

坐标          二进制          最低位

1               0001          1

2               0010          2

3               0011          1

4               0100          4

5               0101          1

6               0110          2

7               0111          1

8               1000          8

...

最低位的计算方法有两种,一种是x&(x^(x–1)),另一种是利用补码特性x&-x。

先根据给定输入数组建立一个树状数组bit,比如,对于 nums = {1, 3, 5, 7, 9, 11, 13, 15, 17},建立出的bit数组为:

bit -> 0 1 4 5 18 11 24 15 74
  • 注意到我们给bit数组开头padding了一个0,这样在利用上面的树状数组的性质时就不用进行坐标转换了。可以发现bit数组中奇数位上的数字跟原数组是相同的,偶数位则是之前若干位之和,符合上述的规律。

  • 现在我们要更新某一位数字时,比如将数字5变成2,即update(2, 2),那么现求出差值 diff = 2 - 5 = -3,然后我们需要更新树状数组bit,根据最低位的值来更新后面含有这一位数字的地方,一般只需要更新部分偶数位置的值即可。由于我们在开头padding了个0,所以我们的起始位置要加1,即 j=3,然后现将 bit[3] 更新为2,然后更新下一位,根据图中所示,并不是bit[3]后的每一位都需要更新的,下一位需要更新的位置的计算方法为 j += (j&-j),这里我们的j是3,则 (j&-j) = 1,所以下一位需要更新的是 bit[4],更新为15,现在j是4,则 (j&-j) = 4,所以下一位需要更新的是 bit[8],更新为71,具体的变换过程如下所示:

0 1 4 5 18 11 24 15 74

0 1 4 2 18 11 24 15 74

0 1 4 2 15 11 24 15 74

0 1 4 2 15 11 24 15 71
  • 接下来就是求区域和了,直接求有些困难,若能求出前i-1个数字之和,跟前j个数字之和,那么二者的差值就是要求的区间和了。所以先实现求前任意i个数字之和,当然还是要利用树状数组的性质,此时正好跟update函数反过来,我们的j从位置i开始,每次将bit[j]累加到sum,然后更新j,通过 j -= (j&-j),这样就能快速的求出前i个数字之和了,从而也就能求出任意区间之和了

代码

class NumArray {
private:
    vector<int> num;
    vector<int> bit;
public:
    NumArray(vector<int>& nums) {
        num.resize(nums.size()+1);
        bit.resize(nums.size()+1);
        for(int i=0;i<nums.size();i++){
            update(i,nums[i]);
        }
    }
    
    void update(int i, int val) {
        int diff=val-num[i+1];
        for(int j=i+1;j<num.size();j+=(j&-j)){
            bit[j]+=diff;
        }
        num[i+1]=val;
    }
    
    int sumRange(int i, int j) {
        return getSum(j+1)-getSum(i);
    }
    
    int getSum(int i){
        int res=0;
        for(int j=i;j>0;j-=(j&-j)){
            res+=bit[j];
        }
        return res;
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * obj->update(i,val);
 * int param_2 = obj->sumRange(i,j);
 */

参考文献

[LeetCode] Range Sum Query - Mutable 区域和检索 - 可变

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

农民小飞侠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值