树状数组

树状数组网上的解释各种各样,通过昨晚跟今天一下午的努力终于把树状数组基本操作会了。对于树状数组相对于线段树,代码长度小了很多,也没有线段树那么繁琐,但是树状数组可能更难理解一些(我是这么认为的),因为有了二进制的加入,可能很多学习者不想去将十进制变成二进制然后推规律,于是我通过将每个操作进行输出(可以免得让自己推大笑)进一步加深了对树状数组的理解。

因为我的这篇关于树状数组的博客,是通过同学给的一张图片资料知道如何建树等等的,因此如果您要学习,建议先去百度百科,或者其他大佬的博客先了解一下树状数组基本的知识再来看,这样您就不会看起来一头雾水了。

因为我个人语文能力有限,单纯的像别的博客讲怎么都讲不清,而且,没有代码无法更加形象的讲述。于是我在前面就不对其具体定义进行详细介绍了,直接附上代码。

因为平时做题的习惯,喜欢将代码的每一句的意思都写在旁边,这样,在看代码时也会轻松一些,不用去上面看一眼解释,再看一眼代码了。

下面附上代码,代码旁边都有着详细的解释,只要耐心去看就行了。

//复杂度:单点修改log(n),区间查询log(n)
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,u,v;
int a[101],c[101];


inline int lowbit(int i)//lowbit运算,表示2的x次方(x表示i的二进制中从右往左数有连续“0”的个数) 
{
	return i&(-i);//-i表示将二进制反转,1变成0,0变成1,最后再在末尾加上1(若最后一位为1就满2进1) 
}


int getsum(int i)//前i个数的和 
{
	int sum=0;
	while(i>0)
	{
		cout<<"i="<<i<<endl;
		printf("lowbit(%d)=%d\n",i,lowbit(i));
		sum+=c[i];
		i-=lowbit(i);
		//当前点减去他的叶子个数就等于c[i]他没有向前包括的点的个数,当他还有没向前包括的点就继续循环 
		//从上往下寻找 
		//x-=x&(-x)可得到子节点,不断循环到0则可得到所有的x所有c[x]加起来即为原本前x个元素的和
	}
	return sum;
}


void Update(int i,int x)//将第i位加上x 
{
	while(i<=n)
	{
		cout<<"i="<<i<<endl;
		printf("lowbit(%d)=%d\n",i,lowbit(i));
		c[i]+=x;
		i+=lowbit(i);
		//主程序中写到 “通过输出lowbit我们不难发现,其实lowbit的值就是当前i的叶子个数 ”也就说明了,当前点的父亲,就等于当前点i加上他的叶子个数
		//也就是他的父亲减去他的叶子数就等于他自己 
		//从下往上更新 
		//x+=x&(-x)可得到c[x]的所有父亲节点
		//通过这个我们也可以得出哪些点与当前点(还有需要更改的点)有存在父亲,爷爷,祖宗的直系亲属关系 
	}
}




int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		int x=i,k=0,y;
//		while(x%2==0)k++,x/=2;
//		y=i-pow(2,k)+1;
		y=i-lowbit(i)+1;
		printf("lowbit(%d)=%d\n",i,lowbit(i));
		//通过输出lowbit我们不难发现,其实lowbit的值就是当前i的叶子个数 
		cout<<"y="<<y<<endl;
		cout<<"i="<<i<<endl;
		for(int j=y;j<=i;j++)c[i]+=a[j];
	}
	for(int i=1;i<=n;i++)cout<<c[i]<<" ";
	cout<<endl;
	scanf("%d%d",&u,&v);//把a[u]加上v 
	Update(u,v);
	scanf("%d",&m);//求前m个数的和 
	cout<<getsum(m);
}
因为我只是搞NOIP的,之前学了线段树,因此树状数组就只是稍微涉及了一些基本操作(不涉及二维树状数组),但每一句代码都有解释(有可能也会有误,只是当时个人理解),希望能对您有帮助!

欢迎交流学习:QQ:2537267194

下面还给个图片吧,这是我机房一个同学给我的,自己百度没找出来,因此不知道到底出自何方神圣,总之感谢这位大佬!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值