您的位置:首页 > 其它

树状数组

2015-01-22 00:00 169 查看
摘要: 树状数组

树状数组

##概述

树状数组是在研究压缩算法时被发现,能够高效的解决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] + … + 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;

}


##参考文章

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

http://dongxicheng.org/structure/binary_indexed_tree/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: