树状数组

看图理解树状数组还是很好理解树状数组的:

                         

 

 

1.树状数组是干嘛的:

  解决动态前缀和问题的数据结构   a1+a2+a3....an
 

2.询问: a1+a2+a3...am(m是变化的)

3.修改 ai(1<=i<=n)

 

 

 

 

 

谈谈询问:

设结点编号为x,那么这个结点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素,
d[6]=a5+a6; 110 2^1;        //d是树状数组,a数组是我们题目或者要用的数组,我们可以把它改成树状数组d
d[8]=a1+...a8;  1000 2^3;

比如d[6]是a5+a6   a:那么我们如何知道d[6]是a5+a6?   6的二进制是110 注意红字 那么就是2^1=2也就是它管辖的区间是俩个,看上面的图来理解, b:那么如何知道是a5+a6这俩个?

 比如我们要询问13这个位置的前缀和

叙述询问过程:

1101 = 13  //二进制拆分
拆分:每次去掉最后一个一
1101  d[13]; 2^0
1100  d[12]; 2^2
1000  d[8];  2^3

从上图也可以看出13位置的前缀和是d[13]+d[12]+d[8];

c:那么问题由来了,我们该如何实现这个过程?我们先看一个代码

int lowbit(int x)
{
    return x&(-x);
}

这个代码是啥意思?首先我们要知道一个正整数的负数的补码是这个正整数的原码取反加一

比如13的二进制

                       13的原码   1101
                             补码    0010+1 = 0011

                       二进制&: 1101
                                      &0011
                                        0001

然后用原码减去&的结果:1101
                                       -0001
                                        1100

可以看出和上次的叙述过程是一样的那么就循环做这个事不就行了,一直到0结束那么就有了查询的代码实现了:

int query()//查询x位置的前缀和
{
    int res=0;
    while(x){
        res+=d[x];
        x-=lowbit(x);
    }
    return res;
}

虽然写出来,但是我们可以看看这个过程的巧妙性:

还是以13举例子:当我们&出来的结果我们可以发现它是末尾少个一的,不就和叙述过程一样么,这点是很巧妙的

 

 

 

 

 

谈谈修改:

如果我们想修改某一树状数组的结点,那么动一点,包含它的点也得动才行啊,那么这个问题怎么解决?

其实询问是按照每个结点往下询问,一直到结束,那么修改不就是往上么,一直上到包含这个点的结点结束不就行了,那么代码就可以实现出来了:

void add(int x)//修改操作 其实就把查询操作到过来
{
    while(x<=n){
        d[x]+=v;
        x+=lowbit(x);
    }
}


树状数组单次查询复杂度0(logn);

 

 

 

然后可以看一题例题:树状数组简单例题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值