浅谈树状数组

树状数组是一个十分高效的数据结构,当要频繁的对数组元素进行修改,同时又要频繁的查询数组内任一区间元素之和的时候,可以考虑使用树状数组。

一般来说,树状数组能做到的线段树都能做到,但线段树能做到的树状数组不能做到,但树状数组相对于线段树代码复杂程度低(总代码也就线段树一个函数那么长),速度更加快。

树状数组的核心是一个叫做lowbit的函数,这个函数的功能是把一个整数的二进制中最低的1这一位取出来,根据补码原理我们只要这样做就行了。

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

具体的原理这里就不解释了,你可以自己用几个数算一下。

树状数组

来观察这个图:
令这棵树的结点编号为C1,C2…Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8

这里有一个有趣的性质:
设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
所以很明显:Cn = A(n – 2^k + 1) + … + An

2^k实际上就是上面所说的lowbit()

修改

当我们需要对树进行修改时,只需将修改的这个点的所有祖先,一个点的祖先即是x+lowbit(x)

void Modify(int n, int delta)  
{  
    while(n <= N)  
    {   
        c[n] += delta;   
        n += lowbit(n);  
    }  
}  

求和

当我们需要对树进行儿子时,只需求这个点的所有儿子的和,一个点的儿子即是x-lowbit(x)

int Sum(int n)  
{  
    int result = 0;  
    while(n != 0)  
    {   
        result += c[n];   
        n -= lowbit(n);   
    }  
    return result;  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值