树状数组

树状数组

概述

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

特点

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

  • 其思想与线段树相似

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

原理

树状数组

观察上图可以得到如下规律:

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满足:

C[i] = A[i–2^k+ 1] + … + Ai

则我们称C为树状数组.

给定i,如何求2^k?

答案很简单:

2k=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;

}

参考文章

  • http://zh.wikipedia.org/wiki/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84

  • http://dongxicheng.org/structure/binary_indexed_tree/

你可能感兴趣的:(树状数组)