树状数组

树状数组


##概述算法

树状数组是在研究压缩算法时被发现,可以高效的解决RMQ问题,能将朴素方式解决RMQ问题的O(n)的时间开销下降为O(lgn)。数组

##特色ui

  • 查询和更改都为O(lgn),code

  • 其思想与线段树类似ip

  • 空间开销为O(n),比线段树小,但不能解决区间最值问题.get

##原理it

树状数组

观察上图能够获得以下规律:原理

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
...
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16

因而能够总结出,给定序列(数列)A,咱们设一个数组C知足:im

C[i] = A[i–2^k+ 1] + … + A[i] (k为i中末尾0的个数)总结

则咱们称C为树状数组.

给定i,如何求2^k?

答案很简单:

2^k=i&(i^(i-1)) = i&(-i)

因而能够用C语言以下实现该功能:

int lowbit(int x)

{

    return x&(-x);

}

###求和

如图,由于C[i] = a[0] + a[1] + ..a[i],因此i~j的和为 C[j] - c[i - 1],所以将求区间和问题转化为求前K项和。

int sum (int k)

{

   int ans = 0;

   for (int i = k; i > 0; i -= lowbit(i))

       ans += BIT[i];

   return ans;

}

###构建

定义一个数组 BIT,用以维护A的前缀和,则:

具体能用如下方式实现:(C++)

void build()

{

    for (int i=1;i<=MAX_N;i++){

         BIT[i]=A[i];

         for (int j=i-1; j>i-lowbit(i);j-=lowbit(j))

                BIT[i]+=BIT[j];

   }

}

###更改

根据图示,能够看出更新实际是从被变动的叶子开始回溯至根节点变沿途更新记录的过程.

void edit(int i, int delta)

{

   for (int j = i; j <= MAX_N; j += lowbit(j))

          BIT[j] += delta;

}

##参考文章

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息