树状数组----算法高级专题
来源:互联网 发布:福建安溪如意茶厂淘宝 编辑:程序博客网 时间:2024/05/21 18:40
lowbit (i):i&-i 或者i=i&(i^i-1)
9 and -1
1、要点:一个数i对应二进制表示未尾0的个数为k,那么他管辖的范围为:从i到左边i-2(k)+1的2(k)个元素。如:i:6 110 k=1 6管辖的范围为:
C[5]=a[5]+a[6]; 2(1) 管2个。
2、求前N个元素的和,N的二进制的最后一个1减去。如:N=7;111à110(6)->100(4)->0
i=i-lowbit(i)
C[7]=a[4]+a[6]+a[7]
C[8]= 1项 a[8] 1000->0
C[i]=c[i]+c[i-2(k)]
K表示i当前未尾0的个数
-àc=7 k=0; c=7-2(0)=7-1=6
C[7]=c[7]
--》k表示未尾0的个数
C=6 k=1 c=6-2(1)=4
C[7]=c[7]+c[6]
--》c=4,k表示未尾0的个数
C=4.k=2; c=4-2(2)=0
C[7]=c[7]+c[6]+c[4]
C=13à1101->第一种思想:
C[13]=1101c[1100=12]=c[1000=8]=c[0]
C[13]=c[13]+c[12]+c[8]
3、如果要修改a[i],应该要修改:
i=i+2(k) C[i].一直到根结点。3 11 ->4->8->16
如:i=15 a[15] 1111 c[i]=15+2(0)=15+1=16
4、动态求和:如果要求a数组中某段元素的和,则只需要统计c数组即可。
《1》sum=sum+c[i]
《2》i=i-lowbit(i)
重复直到i等于0
i=7 要求 a[1]~a[7]的和
sum=0+c[7] i=7-2(0)=6 sum=c[7]+c[6]+c[4] i=6-2(1)=4 4=4-2(2)=0
a[2]到a[7]可以先求a[1]到a[7]-c[2]
树 状 数 组
1、i=i+lowbit(i)向上走,用于更新a数组 ->c[i]
//解释i=i+lowbit(i)表示把i未尾1补0的过程。
2、i=i-lowbit(i)用于求a[1]到a[i]的和,可以通过求c[i]的和来得.
//解释i=i-lowbit(i)表示把i的最后一个1减去。
1、概述
树状数组(binary indexed tree),是一种设计新颖的数组结构,它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组{a}中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和?
2、树状数组基本操作
传统数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结构转换成伪树状结构(线性结构只能逐个扫描元素,而树状结构可以实现跳跃式扫描),使得修改和求和复杂度均为O(lgn),大大提高了整体效率。
给定序列(数列)A,我们设一个数组C满足
C[i] = A[i–2^k+ 1] + … + A[i]
其中,k为i在二进制下末尾0的个数,i从1开始算!
则我们称C为树状数组。
下面的问题是,给定i,如何求2^k?
答案很简单:2^k=i&(i^(i-1)) ,也就是i&(-i)
下面进行解释:
以i=6为例(注意:a_x表示数字a是x进制表示形式):
(i)_10 = (0110)_2
(i-1)_10=(0101)_2
i xor (i-1) =(0011)_2
i and (i xor (i-1)) =(0010)_2
2^k = 2
C[6] = C[6-2+1]+…+A[6]=A[5]+A[6]
数组C的具体含义如下图所示:
当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,
i=i-2(i)
i=i+2(i)表示把未尾1补0的过程如:i=4;
a[4] à 10000 c[i] i=i+lowbit(i)(2k) àlowbit(i)=i&i^(i-1)=i&(-i)=2(k)
a[5]à i=5 101 i=5+1=6 110 ->i=6+2=8 1000->i=
i=13 加2(0)=14 +2(1)=16
a[7]被修改:i=7 111 i=i+2(0)=7+1=8 1000 i=i+2(3)=16
这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。
树状数组能快速求任意区间的和:A[i] + A[i+1] + … + A[j],设sum(k) = A[1]+A[2]+…+A[k],则A[i] + A[i+1] + … + A[j] = sum(j)-sum(i-1)。
下面给出树状数组的C语言实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//求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);
}
}
3、扩展——二维树状数组
一维树状数组很容易扩展到二维,二维树状数组如下所示:
C[x][y] = sum(A[i][j])
其中,x-lowbit[x]+1 <= i<=x且y-lowbit[y]+1 <= j <=y
(2) 二维树状数组:
一个由数字构成的大矩阵,能进行两种操作
1) 对矩阵里的某个数加上一个整数(可正可负)
2) 查询某个子矩阵里所有数字的和
要求对每次查询,输出结果
5、总结
树状数组最初是在设计压缩算法时发现的(见参考资料1),现在也会经常用语维护子序列和。它与线段树(具体见:数据结构之线段树)比较在思想上类似,比线段树节省空间且编程复杂度低,但使用范围比线段树小(如查询每个区间最小值问题)。
C++源程序L
#include<iostream>
#include<fstream>
using namespace std;
ifstream in("树状数组.in");
ofstream out("树状数组.out");
#define max 16
int n,a[max],c[max],m;
int lowbit(int i)
{
return (i&-i);
}
void genxin(int i,int x)
{
while(i<=n)
{
c[i]=c[i]+x;//等于前面已有的元素加上现在读入的数
i=i+lowbit(i);//i未尾补0
}
}
int getsum(int i)
{
int sum=0;
while(i>0)
{
sum=sum+c[i];//累和
i=i-lowbit(i);//把i未尾的1减去
}
return sum;
}
void init()
{
in>>n;
for(int i=1;i<=n;i++)
{
in>>a[i];
genxin(i,a[i]);
}
}
void print()
{
cout<<"原数组为:";
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
cout<<endl<<" 树状数组为: ";
for(int i=1;i<=n;i++)
cout<<c[i]<<" ";
cout<<endl;
}
int main()
{
init();
print();
cout<<"输入m求和"<<endl;
cin>>m;
cout<<getsum(m)<<endl<<endl;
system("pause");
}
- 树状数组----算法高级专题
- 【专题属性】树状数组
- 树状数组专题【完结】
- 【专题】树状数组
- 树状数组专题
- 树状数组专题
- 【树状数组】专题+模板
- 树状数组专题
- 树状数组专题
- 树状数组专题
- 【专题】树状数组
- 树状数组专题
- 树状数组专题【完结】
- 树状数组专题(一)POJ2352
- 树状数组专题(五)hdu1166
- 树状数组专题(六)hdu1556
- 【专题】树状数组(完整版)
- 【专题】线段树&&树状数组
- DownloadProvider 源码详细分析
- TR069协议向导—— 一个帮助你了解TR069协议的简明教程(二)
- Ralph Lauren Rea A wee
- Flume-0.9.4和Hbase-0.96整合(1)
- C++编码规范 读书笔记
- 树状数组----算法高级专题
- apache-tomcat-7 设置最大上传.war文件大小
- 《计算机文化基础》
- webview功能介绍,以及利用缓存节省流量
- 数据结构上机实验之二分查找
- mysql获取表的下一个主键值
- UIApplication的代理
- UIButton使用plist文件的方法
- 年薪第一的数据库工程师是怎样炼成的