树状数组

树状数组是什么呢,就是长的像树的数组。

这个数组主要解决的问题一般是区间问题,比如区间和,区间最大值,最小值,或者区间值改变后的和这一类的题目。

一般做这类的题目我们用暴力(这个算法忽略)或者线段树,但是线段树开的数组比树状数组的大,线段树开的数组大小是 n>>2 ,树状数组的大小是 n ,在一些极端情况下还是用树状数组比较好。

先说个计算公式,叫lowbit,x= m & ( - m ),假如m=6,x=2,这个意思就是从后往前数二进制第几个数是1,(6)2=110,第二位数1,所以lowbit(6)=2

这个计算公式记住就行了,下面会讲作用

先给个图

图中,A数组就是我们输入的数组,C数组就是算法之后的数组。那么现在来解释C数组怎么来的。

C[1]=A[1]

C[2]=C[1]+A[2]

C[3]=A[3]

C[4]=A[4]+C[2]

........(后面就不写了)

我们发现,二进制尾数为1的C[k]=A[k],尾数为0的C[k]=C[lowbit(m1)+m1]+C[lowbit(m2)+m2]......+C[lowbit(mn)+mn]

比如C[8],lowbit(4)=4,4+4=8,lowbit(6)=2,6+2=8,lowbit(7)=1,7+1=8,而图中C[8]正好是C[4]+C[6]+C[7]

同样其他的C[k]也是这样算的,所以就我们要操作树状数组就要用到之前说的lowbit公式

然后说一下大概的操作吧,主要就是查询和修改

修改比较好了,比如我要修改A[3]的数据,那么C[3], C[4], C[8]都要更新数据,我们就从最底下的开始更新一直到最上面

void update(int x,int add)    //x表示数组A[x],add表示更新的值
{
	while (x<=n)
		{
			num[x]=num[x]+add;    //num[x]就表示C[x]
			x=x+lowbit(x);
		}
	return ;
}

通过之前的演示我们知道C[k]=C[lowbit(m1)+m1]+C[lowbit(m2)+m2]......+C[lowbit(mn)+mn](还一个不复制了

那么如果其中一个值改变了,C[k]也是要改变的,我们就可以用lowbit公式来找到上一层。所以代码中的while就是通过lowbit来找到上一层所在的位置,一直这样找直到最高层

还有就是查询了。查询[ i , j ]范围内的和,我们一般是先找[0, j] 的和在减去[0, i-1]的和

假如我们要找[4,6]的和,我们先找[0,6]的和,但是我们可以看出,C[6]=A[5]+A[6],之前的就没有加上。这时候我们还是要用到lowbit。lowbit(6)=2,很明显我们要找的是C[6-2]=C[6-lowbit(6)]的值,所以只要一直找完C[x-lowbit(x)]就行了(别问我为什么是这样,查了一下其它的博客,说是这样算得出的是上一个父节点。。。不懂

int query(int x)
{
	int sum=0;
	while (x>0)
		{
			sum=sum+num[x];
			x=x-lowbit(x);
		}
	return sum;
}

树状数组大概就是这样子的。

最后说句,输入就是更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值