树状数组

。_饼干妹妹 提交于 2019-12-02 15:23:49

树状数组定义:

是一个查询和修改复杂度都为log(n)的数据结构。可以用于处理前缀和的问题,动态维护前缀和的工具

区间修改和区间查询用树状数组会显得很麻烦 相对而言用线段树会更灵活。

基本操作:求数列区间和,可以对数列单点进行操作。

前置知识:

差分数组

lowbit()操作:返回非负整数x 在二进制表示下,第一个1和后面的0表示的数值(十进制的值)。

int lowbit(int x)
{
     return x&(-x);
}
/*
-i 代表i的负数 计算机中负数使用对应的正数的补码来表示k表示i的二进制中末尾连续0的个数。
例如 : i=6(0110) 此时 k=1
-i=-6=(1001+1)=(1010)
 i&(-i)=(0010)=2=2^K k=1.
C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i];
C[i]=A[i-lowbit(i)+1]+A[i-lowbit(i)+2]+......A[i];
*/

树状数组思想:

区间查询——》前缀和  ——》树结构维护(log2n)

树状数组 t[x] 保存以x为根的子树中叶节值的和 。

观察 t[x] 中每个x的二进制,每一层末尾零相同 ,零的个数即K对应覆盖的长度,覆盖长度就是lowbit(x)。

 t [x] 的父节点为 t【x+lowbit[x]】 树的深度为 log2n+1

 

 

基本操作(单点修改 查询前缀和)

void update(int x,int val)//单点更新
{
    while(i<=n){
        t[x]+=val;
        i+=lowbit(i);//由叶子节点向上更新树状数组C,从左往右更新
    }
}
int ask(int x)//求区间[1,i]内所有元素的和 即求前缀和
{
    int ans=0;
    while(x>0){
        ans+=t[x];//从右往左累加求和
        x-=lowbit(i);
    }
    return ans;
}

   

 树状数组的用法

1.单点修改,单点查询    update(x,val) ;  ask(x) - ask(x-1);

2.单点修改,区间查询     update(x,val) ;  ask(r) - ask(l-1);

3.区间修改,单点查询  (差分数组 ) 

 用树状数组维护差分数组的前缀和,即原数列的每个元素,由于区间修改 ,产生的改变量。

 区间修改 [l,r]+d    update(l,d)   update(r+1,-d) 

 查询 a[x]                ans =a[x]+ask(X) 

4. 区间修改,区间查询 

   求出原数列a[x]的前缀和  ans=a[r]-a[l]   原数列的前缀和也可以用差分数组来求 

 

 

 

 参考

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!