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

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

我们常常把树状数组和线段树相比较,一般认为树状数组的处理效率和编写复杂度优于线段树,线段树较树状数组能处理更多的信息,有人提到线段树擅长处理横向区间的问题,树状数组擅长处理纵向区间的问题,总之,在处理区间这类问题上,我们二者都要掌握。树状数组的建树复杂度为O(nlogn),维护和查询复杂度为O(logn)。

树状数组是什么呢?我们用两张图来表示一下:

换言之,就是根据数组下标二进制1的最低位构成的一棵变形树

我们用A数组来表示原始数组,C数组来表示树状数组

0001的最低位在2^0上,C[1]只表示A[1]

0010的最低位在2^1上,C[2]表示A[1]+A[2]

0011的最低位在2^0上,C[3]表示A[3]

0100的最低位在2^2上,C[4]表示A[1]+A[2]+A[3]+A[4] = C[2]+A[3]+A[4] 

0101的最低位在2^0上,C[5]只表示A[5]

0110的最低位在2^1上,C[6]表示A[5]+A[6]

换言之,在树状数组中一个下标最低位为2的几次幂,该下标就指代它往前(包括它)往前数2的几次幂的和

如果我们要计算1到6的和

我们需要计算C[4]+C[6]

学习树状数组时,有一个必须掌握的函数:lowbit

lowbit其实就是取出x的二进制的最低位1,比如0110这个二进制数,lowbit(6) = 2

具体实现如下

1 int lowbit(int n)2 {3     return n & -n;4 }

计算机中负数使用对应的正数的补码来表示

例如 : n=6(0110)
-n=-6=(1001+1)=(1010)
n&(-n)=(0010)=2=2^1

即最低位1在2^1的位置上

 

所以我们计算1到6的和时

就可以先算SUM += C[6]

再6 - lowbit(6) = 4

再算SUM += C[4]

4 - lowbit(14) = 0 结束运算

1 int search(int n) 2 { 3     int sum = 0; 4     while (n) 5     { 6         sum += c[n]; 7         n -= lowbit(n); 8     } 9     return sum;10 }

 

在更新时方向则相反,我们假设原始数据是一个长度为6的数组

我需要更新下标为1的数据是,更新顺序为:

先更新C[1],1+lowbit(1) = 2

再更新C[2],2+lowbit(2) = 4

4+lowbit(4) = 8 > 6

更新停止

 

更新和初始化树状数组函数如下:

void update(int n, int pos, int val){    while (pos <= n)    {        c[pos] += val;        pos += lowbit(pos);    }}void init(int n){    memset(c, 0, sizeof(c));    for (int i = 1; i <= n; i++)        update(n, i, a[i]);}

 

在学习树状数组时,一定时刻注意要用二进制的思想来去思考,而这一算法的优势之处也是在此

这里再附上求1到x和的模板:

1 #include 
2 #include
3 4 using namespace std; 5 6 int a[1050];//原始数据 7 int c[1050];//树状数组 8 9 int lowbit(int n)10 {11 return n & -n;12 }13 14 void update(int n, int pos, int val)15 {16 while (pos <= n)17 {18 c[pos] += val;19 pos += lowbit(pos);20 }21 }22 23 void init(int n)24 {25 memset(c, 0, sizeof(c));26 for (int i = 1; i <= n; i++)27 update(n, i, a[i]);28 }29 30 int search(int n)31 {32 int sum = 0;33 while (n)34 {35 sum += c[n];36 n -= lowbit(n);37 }38 return sum;39 }40 41 int main()42 {43 ios::sync_with_stdio(false);44 int n,m;45 while (cin >> n >> m)46 {47 for (int i = 1; i <= n; i++)48 cin >> a[i];49 50 init(n);51 52 while (m--)53 {54 int t;55 cin >> t;56 cout << search(t) << endl;;57 }58 }59 60 return 0;61 }

如果还是有些懵的话,这里再推荐一个更详细的入门讲解:https://www.cnblogs.com/acgoto/p/8583952.html#4046752

后面我们再详谈求区间最值和逆序对的方法

转载于:https://www.cnblogs.com/qq965921539/p/9609541.html

你可能感兴趣的文章
【项目实施】项目考核标准
查看>>
spring-aop AnnotationAwareAspectJAutoProxyCreator类
查看>>
经典入门_排序
查看>>
Redis Cluster高可用集群在线迁移操作记录【转】
查看>>
二、spring中装配bean
查看>>
VIM工具
查看>>
javascript闭包
查看>>
@Column标记持久化详细说明
查看>>
创建本地yum软件源,为本地Package安装Cloudera Manager、Cloudera Hadoop及Impala做准备...
查看>>
mysql8.0.13下载与安装图文教程
查看>>
站立会议08(冲刺2)
查看>>
url查询参数解析
查看>>
http://coolshell.cn/articles/10910.html
查看>>
[转]jsbsim基础概念
查看>>
DIV和SPAN的区别
查看>>
第一次使用cnblogs
查看>>
C#语法糖之 session操作类 asp.net
查看>>
2015 Multi-University Training Contest 3
查看>>
使用Gitblit 在windows 上部署你的Git Server
查看>>
217. Contains Duplicate
查看>>