前缀和(树状数组)

当我们计算一个数组的前缀和时,我们直接从数组开头加到结尾,sum = a[0] + a[1] + ...+ a[n-1].

sum = 0;
for(int i = 0;i < n;i++)
    sum += a[i];

但是我们要对数组进行修改,再查询前缀和的时候,需要重新加一遍,时间复杂度为O(n),当n很大的时候效率会变得很低,所以我们引入了树状数组,树状数组可以把查询和点更新都在O(logn)的时间内完成,那么树状数组是怎么实现的呢。

树状数组顾名思义,就是用数组来模拟树形结构

 树状数组如图:

由图中可以看到树状数组是一个树形结构,数列的数用数组A[]来存储,同时引入了管理小组C[],管理小组的每个成员存储他的子节点的和,如图:

C[1]:存储A[1];

C[2]:存储C[1],A[2];

C[3]:存储A[3];

C[4]:存储C[2],C[3],C[4]......

树状数组是怎么将前缀和查询降到O(logn)的呢,其实看图可以知道,当我们查询一个节点的前缀和的时候只需要将这个节点左侧的子树的根加起来即可,

sum[4]:左侧没有子树 直接找c[4].sum[4] = c[4];

sum[5]:左侧有一棵子树,其根为c[4],sum[5] = c[4] + c[5]......这样的结构可以把O(n)的复杂度降低,不必每次都从头开始加。

树状数组的点更新其实也很简单,只要将包含该节点的树的树根更新即可,比如上面的树状数组图中,要更新A[3]的值,在树状数组中只要修改包含C[3]的树即可,C[3]:包含于C[4]和C[8],所以只需要修改C[4]和C[8]即可;

树状数组的代码实现:

如何确定树状数组C中存哪个数呢,这里我们介绍一个lowbit操作,这个是根据第i个数的末尾有几个0,C[i]便存前2的i次个数

附上一个特别棒的网站,帮助理解(动画演示代码):
https://visualgo.net/zh/fenwicktree

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

树状数组点更新

void update(int i,int v)
{
    for(;i<=n;i+=lowbit(i))
        c[i] += v;
}

树状数组前缀和

int sum(int i)
{
    int s = 0;
    for(;i>0;i-=lowbit(i))
        s+=c[i];
    return s;
}

树状数组求区间和

int sum1(int l,int r)
{
    int s1 = sum(l-1);
    int s2 = sum(r);
    return s2 - s1;
}


 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值