树状数组

【引言】
在解题过程中,我们有时需要维护一个数组的前缀和S[i]=A[1]+A[2]+...+A[i]。但是不难发现,如果我们修改了任意一个A[i],S[i]、S[i+1]...S[n]都会发生变化。可以说,每次修改A[i]后,调整前缀和S[]在最坏情况下会需要O(n)的时间。当n非常大时,程序会运行得非常缓慢。因此,这里我们引入“树状数组”,它的修改与求和都是O(logn)的,效率非常高。
【理论】

为了对树状数组有个形 象的认识,我们先看下面这张图。


如图所示,红色矩形表示的数组C[]就是树状数组。
这里,C[i]表示A[i-2^k+1]到A[i]的和,而k则是i在二进制时末尾0的个数,或者说是i用2的幂方和表示时的最小指数。( 当然,利用位运算,我们可以直接计算出2^k=i&(i^(i-1)) )同时,我们也不难发现,这个k就是该节点在树中的高度,因而这个树的高度不会超过logn。所以,当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。

接着,我们考察这两种操作下标变化的规律:
首先看修改操作:
已知下标i,求其父节点的下标。我们可以考虑对树从逻辑上转化:


【代码】
求最小幂2^k:

int Lowbit(int t)
{
	return t & (t ^ (t - 1));
}

求前n项和:

int Sum(int end)
{
	int sum = 0;
	while(end > 0)
	{
		sum += in[end];
		end -= Lowbit(end);
	}
	return sum;
}

对某个元素进行加法操作:

void plus(int pos , int num)
{
	while(pos <= n)
	{
		in[pos] += num;
		pos += Lowbit(pos);
	}
}

作者:武钢三中 吴豪


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值