您的位置:首页 > 其它

树状数组

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

)进一步加深了对树状数组的理解。

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

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

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

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

//复杂度:单点修改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

下面还给个图片吧,这是我机房一个同学给我的,自己百度没找出来,因此不知道到底出自何方神圣,总之感谢这位大佬!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息