【基础算法-树状数组】入门-C++
@
基本定义
树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
如何理解树状数组
树状数组,重点在于它是树状的 (这不废话吗)
大家都知道二叉树吧,贴一张二叉树的图给大家理解一下(自己画的有点丑)
我们把它变形一下...
现在定义每一列的顶端结点C[]数组
ps.最后一个图不是我画的
C[i]代表子树的叶子结点的权值之和, 这里以求和举例
如图可以知道
C[1]=A[1];
C[2]=A[1]+A[2];
C[3]=A[3];
C[4]=A[1]+A[2]+A[3]+A[4];
C[5]=A[5];
C[6]=A[5]+A[6];
C[7]=A[7];
C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
这就是树状数组的基本组成。
将C[]数组的结点序号转化为二进制
1=(001) C[1]=A[1];
2=(010) C[2]=A[1]+A[2];
3=(011) C[3]=A[3];
4=(100) C[4]=A[1]+A[2]+A[3]+A[4];
5=(101) C[5]=A[5];
6=(110) C[6]=A[5]+A[6];
7=(111) C[7]=A[7];
8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];
对照式子可以发现 C[i]=A[i-2^ k+1]+A[i-2^k+2]+......A[i];
(k为i的二进制中从最低位到高位连续零的长度)例如i=8时,k=3;可以自行带入验证;
现在引入lowbit(x)
lowbit(x) 其实就是取出x的最低位1 换言之 lowbit(x)=2^k k的含义与上面相同 理解一下
主要操作
添加元素
单点修改
单点查询
区间修改
区间查询
前两个普通数组能够O(1)时间复杂度完成,后两个普通数组需要O(n)时间复杂度完成,而树状数组最大只需要O(logn),这也正是树状数组的快捷之处。
代码实现
0.lowbit操作
int lowbit(int k)
{
return k&-k;
}
不懂的看下上面引用的那一段
1.添加元素
void add(int s,int num)
{
for(long long i=s;i<=n;i+=lowbit(i))
tree[i]+=num;
return;
}
添加元素的操作可能有些不好理解,但同上,只要理解了lowbit操作,基本就能看懂这个添加操作了...
2.单点修改
这个操作普通数组只要O(1)的时间复杂度,但是树状数组需要最高 O(logn)的时间,因为在树状数组中数组中的一些元素是有联系的,修改其中一个就需要牵扯到很多...
void add(int x,int k)
{
while(x<=n)
{
tree[x]+=k;
x+=lowbit(x);
}
}
3.单点查询
依然,普通数组O(1),树状数组最高O(logn)
long long ask(long long s)
{
long long ans=0;
for(long long i=s;i>=1;i-=lowbit(i))
ans+=tree[i];
return ans;
}
4.区间修改
这个操作就是树状数组的强项之一了,普通数组O(n),树状数组也是不到O(logn)跑出来
void add(int s,int num)
{
for(long long i=s;i<=n;i+=lowbit(i))
tree[i]+=num;
}
p.s.把[x,y]区间的数加上s,需要add(x,s);add(y+1,-s);
5.区间查询
这依然是树状数组的强项,时间复杂度同4
int sum(int x)
{
int ans=0;
while(x!=0)
{
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}