树状数组
来源:互联网 发布:nba数据库统计 编辑:程序博客网 时间:2024/06/05 16:26
但是不难发现,如果我们修改了任意一个A[i],S[i]、S[i+1]...S[n]都会发生变化。
可以说,每次修改A[i]后,调整前缀和S[]在最坏情况下会需要O(n)的时间。
当n非常大时,程序会运行得非常缓慢。
因此,这里我们引入“树状数组”,它的修改与求和都是O(logn)的,效率非常高。
【理论】
为了对树状数组有个形 象的认识,我们先看下面这张图。
如图所示,红色矩形表示的数组C[]就是树状数组。
这里,C[i]表示A[i-2^k+1]到A[i]的和,而k则是i在二进制时末尾0的个数,
或者说是i用2的幂方和表示时的最小指数。
( 当然,利用位运算,我们可以直接计算出2^k=i&(i^(i-1)) )
同时,我们也不难发现,这个k就是该节点在树中的高度,因而这个树的高度不会超过logn。
所以,当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,
这个操作的复杂度在最坏情况下就是树的高度即O(logn)。
另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。
不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,
因此,求和操作的复杂度也是O(logn)。
接着,我们考察这两种操作下标变化的规律:
首先看修改操作:
已知下标i,求其父节点的下标。
我们可以考虑对树从逻辑上转化:
如图,我们将子树向右对称翻折,虚拟出一些空白结点(图中白色),将原树转化成完全二叉树。
有图可知,对于节点i,其父节点的下标与翻折出的空白节点下标相同。
因而父节点下标 p=i+2^k (2^k是i用2的幂方和展开式中的最小幂,即i为根节点子树的规模)
即 p = i + i&(i^(i-1)) 。
接着对于求和操作:
因为每棵子树覆盖的范围都是2的幂,所以我们要求子树i的前一棵树,只需让i减去2的最小幂即可。
即 p = i - i&(i^(i-1)) 。
至此,我们已经比较详细的分析了树状数组的复杂度和原理。
在最后,我们将给出一些树状数组的实现代码,希望读者能够仔细体会其中的细节。
【代码】
求最小幂2^k:
int Lowbit(int t)
{
return t & ( t ^ ( t - 1 ) );
}
求前n项和:
int Sum(int end)
{
int sum = 0;
while(end > 0)
{
sum += in[end];
end -= Lowbit(end);
}
return sum;
}
对某个元素进行加法操作:
void plus(int pos , int num)
{
while(pos <= n)
{
in[pos] += num;
pos += Lowbit(pos);
}
}
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- WEB前端开发 解决IE6、IE7、IE8样式不兼容问题
- 个人总结 VS一些基础技巧
- 错误: [UILabel copyWithZone:]: unrecognized selector sent to instance
- omapl138 ubifs文件系统移植 之U-boot移植一
- 使用事件驱动模型实现高效稳定的网络服务器程序
- 树状数组
- 对jdbc进行封装遇到的问题(一)
- 335. Old sin makes new shame. 一失足成千古恨
- 再度牵手webservice—验证磨刀不误砍柴工的真理
- 对jdbc进行封装(二)
- Google C++ 编程规范 1.3
- 11-16这是昨晚的 贪吃蛇 container map food snake 几个div的构造如下(功能有待实现)
- VS2010 Asp.net rdlc报表 拥有多个数据源
- HTML5时代的来临