307. Range Sum Query - Mutable

来源:互联网 发布:moe软件官网 编辑:程序博客网 时间:2024/06/05 15:08

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.

s思路:
1. 本来之前求和,是先o(n)把所有的和从0开始都求出来,然后查询的时候,就o(1)查询了。这么做的原因,是给定的数据不变。现在题目变了,给的数据允许发生变化,而且数据变化的次数还多,说一要是每次都求和,那么复杂度就太大了,例如:n次操作中,一半是更新数据,一半是查询range sum,那么复杂度就是o(n*n/2+n/2),最后复杂度就是o(n^2)。
2. 之前的方法就行不通了,因为:两个操作(update, query)都一样重要,所以不能出现o(n)的操作了,最好是能平衡一下,让o(n)变快一些成为o(lgn),而o(1)变慢一些,成为o(lgn),最后总的复杂度就是o(nlgn)
3. 这道题还是很出名的。引入bit,binary indexed tree,可以做到更新和查询都是o(lgn)。再次看出来vector这种数据结构真心弱爆了,稍微麻烦点的事都不行!
4. 晚上回去翻笔记先。
5. 再次说明了学到新的东西,有兴趣的东西,一定要做笔记。幸亏自己之前对BIT做了笔记,记录了当时自己的理解和当时的思路。现在再看的时候,马上就能进入到当初的情景中去,而且再看的时候,还能在过去的理解上有新的理解;而如果没有笔记又重新去搞懂的话,费时间不说,而且最多回到过去理解的程度,想理解得更深刻是很难的了。
6. 把纸质的笔记搬到这里:
这里写图片描述
上图,编码过程如下:

c1--->a1 c1的index=0b001c2--->a1+a2 c2的index=0b010c3--->a3 c3的index=0b011c4--->a1+a2+a3+a4 c4的index=0b100c5--->a5 c5的index=0b101c6--->a5+a6 c6的index=0b110c7--->a7 c7的index=0b111c8--->a1+a2+a3+a4+a5+a6+a7+a8 c8的index=0b1000

b编码的过程依赖c的index的值,具体说,就是index的从右往左看第一个1的位置,例如:c4的index=0b100,从右往左看第一个1的位置表示4,所以表示c4等于前四个数之和;c7的index=0b111,从右往左看第一个1的位置表示1,所以第七个数。这是表示的方法。
7. 现在来看如何update?如果a1变化了,如何更新c对应的编码值呢?需要注意的是,更新是从小坐标到大坐标,即:从1开始往8flood.先把c1=a1更新,然后计算下一个更新的坐标,1+(1从右往左看第一个1的位置所表示的值)=1+1=2,现在更新c2,然后计算下一个更新的坐标,2+(2从右往左看第一个1的位置所表示的值)=2+2=4,现在更新c4,再计算新坐标,4+(4从右往左看第一个1的位置所表示的值)=4+4=8。也就是说,a1变化了,只需更新lg(n)个值,这正是我们需要的呀!
8. 现在看如何求和?比如要求[1,7],怎么求?求和的过程和update正好相反,从大坐标往小的坐标遍历。即:先加c7,然后7-(7从右往左看第一个1的位置所表示的值)=7-1=6;再加c6,然后6-(6从右往左看第一个1的位置所表示的值)=6-2=4;再加c4,然后4-(4从右往左看第一个1的位置所表示的值)=4-4=0,等于0说明加完了,求和结果就得到了,你看,求和的时候仍然是操作o(lgn)次就够了。
9. 这个update的过程就像编码的过程,而求和的过程就类似解码过程。而且,正如名字说的,binary indexed tree,这个结构是基于数的index来编码的,而且是一个树结构,因此,更新一个数,就是从树的底层往顶层spread; 求和,则是从树的顶层往底层spread. 再次看出,树的结构比vector更灵活!
10. 还有一个trick的地方,求任意数i,从右往左看第一个1坐在位置对应的值,不需要一位一位的来移位看,这个方法太细节了,还有更简洁的top-down的数学的方法,j&(-j)即可。

class NumArray {private:    vector<int> num,BIT;public:    NumArray(vector<int> nums) {        num.resize(nums.size()+1,0);        BIT.resize(nums.size()+1,0);             for(int i=0;i<nums.size();i++){            update(i,nums[i]);          }       }    void update(int i, int val) {        int diff=val-num[i+1];        num[i+1]=val;        for(int j=i+1;j<BIT.size();j+=j&(-j)){            BIT[j]+=diff;            }    }    int getSum(int idx){        int sum=0;        for(int i=idx+1;i>0;i-=i&(-i)){            sum+=BIT[i];            }           return sum;    }        int sumRange(int i, int j) {        return getSum(j)-getSum(i-1);    }};/** * 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); */
0 0
原创粉丝点击