Leetcode (307) Range Sum Query - Mutable

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:
The array is only modifiable by the update function.
You may assume the number of calls to update and sumRange function is distributed evenly.

题意
  • 给定一个数组,要求对数组进行两种操作
    1. 给定区间范围,求出该区间的和
    2. 更新数组中的一个值
思路
  • 暴力,记录数组,查询什么区间和,立即O(n)扫描一遍计算出来,这样更新的话直接更新即可,操作可以直接O(1)完成
  • 显然,对于更新频繁而查询极少的情况来说,速度的确非常快,但是,对于查询非常频繁的情况,这就会非常慢了(测试样例中应该包含大量的查询、更新操作),因此需要对查询进行优化
  • 注意到,当查询非常频繁的时候,可能有大量重合的查询,如(1, 10),(2, 20),其中,中间(2, 10)部分可以说是经过了两次计算,这样的计算其实是可以优化的,而对于更新操作,也只需要计算修改位置的差值,然后更新区间和即可
  • 当然,要是记住所有区间的和,也并不现实(这样更新操作复杂度太高)
  • 那么,就有一种数据结构能够有效地维护区间和,称为Binary Index Tree,是线段树的一种简化形式
  • 它的基本思想也是事先处理一部分区间和,避免重复查询计算
  • 直观思路在于对于需要维护的区间和数组bit[N],利用下标即可标记数组和,如
    00012
    ,则该二进制数转为十进制为1,而二进制表示中,末尾有0个0,所以它表示为从下标1开始
    20=1
    个数的和,即bit[1] = numbers[1](PS: numbers下标从1开始)。又如
    10002
    ,十进制表示为8,而二进制表示末尾有3个0,因此代表
    23=8
    个数字的和,即numbers[1]+…+numbers[8]。
  • 综合来说,即对于十进制数
    x
    <script type="math/tex; mode=display" id="MathJax-Element-1103">x</script>,其二进制表示末尾若有m个0,则bit[x] = numbers[x]+numbers[x-1]+..+numbers[x-2^m+1]
  • 具体如图所示:
    这里写图片描述

  • 时间复杂度方面,对于m次操作,n个数的数组,初始化为O(nlogn),查询一次为O(logn),

  • 对于更新操作,只需要跟原数组对比,对于所有有关那个数的区间和,全部更新一次即可,复杂度为o(logn)
  • 所以总的复杂度为O(max(m, n)logn)
  • PS:这是定长数组的一般算法,对于不定长数组,即包括增删数字,那分两种情况,一种只在末尾增删,那一样可以处理,因为可以把原数组末尾的数都设为0,增删只是加减操作而已,另一种情况,对于增删中间的数的话,则一般不能使用BIT了
  • 代码如下
#define N 1000000+10
int ___vec[N];
int bit[N];
int n;


class NumArray {
public:
    NumArray(vector<int> &nums) {
        n = nums.size();
        memset(bit,0,sizeof(bit));
        memset(___vec,0,sizeof(___vec));
        for (int i=0;i<n;i++)
        {
            update(i,nums[i]);
        }
    }

    void update(int i, int x) {
        i++;
        int y=x;
        x -= ___vec[i];
        ___vec[i] = y;
        while (i<=n)
        {
            bit[i] += x;
            i += i&-i;
        }
    }

    int sum(int i)
    {
        int s=0;
        while (i>0)
        {
            s += bit[i];
            i -= i&-i;
        }
        return s;
    }

    int sumRange(int i, int j) {
        j++;
        if (i==0) return sum(j);
        else return sum(j)-sum(i);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值