当前位置:   article > 正文

高级数据结构3--树状数组_树状数组预处理后o(1)查询

树状数组预处理后o(1)查询

本文是高级数据结构系列第3篇。

从这一篇开始,是一些区间数据结构。

引入

维护一个数列a,开始时数列中每一个数都是ai,支持以下两种操作:

  • A x y:表示把ax加上y
  • Q l r:表示询问i=lrai的值。

数列长度为n,操作总个数为m(n<=106,m<=106)

使用各种数据结构的时间复杂度如下表:

原数组前缀和树状数组
A操作O(1)O(n)O(log(n))
Q操作O(n)O(1)O(log(n))

从上表看出,无论是对于哪种操作,树状数组都有不俗的时间复杂度表现,因此,树状数组擅长解决这个问题。

介绍

树状数组

来观察一下这个图。

我们假设原数组是{a},而树状数组是{c}。则c1=a1c2=a1+a2c3=a3c4=a1+a2+a3+a4,…在这个图中,c12a9a12的和,c8i=18ai,其它以此类推。

如果我们把c数组中的下标用二进制表示出来的话,可以发现,ci中包含的a中数的范围为i-(i二进制表示中实际数值最小的那个1)+1到i。例如,当i=12时,其二进制表示为1100,最低位上的1是左数第二个1,它表示的实际数值为(100)2=(4)10,所以包含的a中数的范围为9-12。

以上内容部分参考百度百科

然后问题就是,怎么知道这个实际数值呢?

根据二进制补码的原理,我们知道12&(12)=4,这里的&是位与符号。方便起见,我们把i&(i)叫做lowbit(i)。即ci=j=ilowbit(i)+1iai

修改与查询

修改

讲了这么多,终于进入正题了。再看一眼上面那个图:

树状数组

如果我们要把a7加1,那么c中的那些数也要加1呢?

答案是,c7c8c16。因为这些都包含了a7

根据观察,发现下标每次增加的量也是lowbit(i)。容易写出以下代码:

inline int lowbit(int x){
    return x&-x;
}
inline void add(int x,int y){
    //向a[x]加y。
    while (x<=n){
        c[x]+=y;
        x+=lowbit(x);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

查询

和修改十分类似。自己观察上图。

inline int ask(int x){
    int ans=0;
    while (x){
        ans+=c[x];
        x-=lowbit(x)
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

应用

  • 求逆序对
  • 等等
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/654171
推荐阅读
相关标签
  

闽ICP备14008679号