博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
饭后粗谈树状数组
阅读量:5244 次
发布时间:2019-06-14

本文共 1110 字,大约阅读时间需要 3 分钟。

今天我们来讨论一种我自认为十分实用的数据结构——树状数组。在我看来树状数组十分易懂,一定程度上与线段树相比有极大的优势。接下来我们来深究树状数组。

前提

你不需要会线段树,但你需要知道线段树的存贮结构(因为我将通过比较线段树来讲解)。

正文

我们先来见识一下线段树的储存方式:

应该很好懂,接下来就是主角登场了:

当当当当……是不是觉得很优美?什么?你居然否认!哼哼,等我讲完你就会觉得优美了。

下面是树状数组的存储方法(敲黑板):以上方的树为例,我们令八个子节点的权值各为a[1],a[2],a[3]....a[8];然后定义c[i]代表子树的叶子结点的权值之和:

有图有真相,相信大家都已经看懂了。

接下来就是树状数组的构建,请大家先拿出草稿纸。我们可以发现,每一个c[i]都在一个节点的左儿子上,所以对照式子可以发现c[i]=a[i-2^k+1]+a[i-2^k+2]+……+a[i](k为i的二进制中从最低位到高位连续零的长度),所以我们需要求出k,至于怎么求,这就是为什么要你们拿草稿纸的原因,大家可以自行推算,这里我先把答案亮出来吧:我们引入lowbit(x)求k,具体操作就是t&(-t),为了方便,不建议大家写一个函数,可以直接定义:#define lowbit(t) t&(-t)  (如果你是P党,那么恕我救不了你)。现在树状数组的核心已经讲述完成了,接下来的就十分简单了,大家可以打开网易云音乐,搜索用户落九思,进入他的歌单,一边听歌一边学树状数组。对于树状数组的构建,我们可以把c全部看成权值为0,那么,构建操作就成了单点修改,add(i,a[i])即可,而单点修改,当c[i]被更改时,我们发现c[i]的祖先上的c也需要更改,然而有一个好消息:下一个需要修改的祖先的c数组的编号为i+lowbit[i](大家可以拿出草稿纸证一下),于是,我们就顺理成章的能写出add了:

void add(int x,int y){for(int i=x;i<=n;i+=lowbit(i))c[i]+=y;}

至于剩下的区间查询,不过是add的反向操作,我此处就不细讲了,留给大家思考。但此处还是给出代码:

int getsum(int x){int ans=0;for(int i=x;i>0;i-=lowbit(i))ans+=c[i];return ans;}
于是乎,树状数组就讲完了,它的时间复杂度为nlogn,它在一些情况下可以代替线段树,取得更好的效果,但它不支持区间修改,所以慎用,慎用。

转载于:https://www.cnblogs.com/ninsier15/p/9787115.html

你可能感兴趣的文章
CentOS
查看>>
Linux pipe函数
查看>>
java equals 小记
查看>>
爬虫-通用代码框架
查看>>
2019春 软件工程实践 助教总结
查看>>
YUV 格式的视频呈现
查看>>
Android弹出框的学习
查看>>
现代程序设计 作业1
查看>>
在android开发中添加外挂字体
查看>>
Zerver是一个C#开发的Nginx+PHP+Mysql+memcached+redis绿色集成开发环境
查看>>
多线程实现资源共享的问题学习与总结
查看>>
Learning-Python【26】:反射及内置方法
查看>>
torch教程[1]用numpy实现三层全连接神经网络
查看>>
java实现哈弗曼树
查看>>
转:Web 测试的创作与调试技术
查看>>
python学习笔记3-列表
查看>>
程序的静态链接,动态链接和装载 (补充)
查看>>
关于本博客说明
查看>>
线程androidAndroid ConditionVariable的用法
查看>>
stap-prep 需要安装那些内核符号
查看>>