查看文章
 
树状数组
2010年03月21日 星期日 0:27

树状数组可以用来干嘛?树状数组可以用来快速求一个数组的和。。。。

当然这只是片面的,树状数组最大的好处就是支持处理一些动态的问题,它是一种高效的进行区间统计的数据结构。

它支持查询和修改操作,并且复杂度都是log(n)级别的,可见树状数组的高效。

看图说话(摘自百度百科)

看懂了这个图就基本掌握了树状数组了。

来观察这个图:

令这棵树的结点编号为C1C2...Cn。令每个结点的值为这棵树的值的总和,那么容易发现:

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

这里有一个有趣的性质:

设节点编号为x,那么这个节点管辖的区间为2^k(其中kx二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax

所以很明显:Cn = A(n 2^k + 1) + ... + An

算这个2^k有一个快捷的办法,定义一个函数如下即可:

int lowbit(int x){

return x&(x^(x1));

}

当想要查询一个SUM(n)时,可以依据如下算法即可:

step1:sum = 0,转第二步;

step2:假如n <= 0,算法结束,返回sum值,否则sum = sum + Cn,转第三步;

step3: n = n lowbit(n),转第二步。

可以看出,这个算法就是将这一个个区间的和全部加起来,为什么是效率是log(n)的呢?以下给出证明:

n = n lowbit(n)这一步实际上等价于将n的二进制的最后一个1减去。而n的二进制里最多有log(n)1,所以查询效率是log(n)的。

那么修改呢,修改一个节点,必须修改其所有祖先,最坏情况下为修改第一个元素,最多有log(n)的祖先。

所以修改算法如下(给某个结点i加上x):

step1: i > n时,算法结束,否则转第二步;

step2: Ci = Ci + xi = i + lowbit(i)转第一步。

i = i +lowbit(i)这个过程实际上也只是一个把末尾1补为0的过程。

对于数组求和来说树状数组简直太快了!


类别:知识||添加到搜藏 |分享到i贴吧|浏览(235)|评论 (0)
 
最近读者:
 
网友评论:
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
     

   
帮助中心 | 空间客服 | 投诉中心 | 空间协议
©2012 Baidu