C++实用数据结构:二叉索引树

看下面这个问题(动态连续和查询):

有一个数组A(长度为n),要求进行两种操作:

add(i,x):让Ai增大x;

query(a,b):询问Aa+Aa+1+...+Ab的和;

若进行模拟,则每次query操作的最坏的时间复杂度为O(n),在n较大时速度较慢。用前缀和也不能提高效率(每次add操作最坏为O(n))。有一种数据结构,可以在O(n)时间里初始化,用O(logn)的速度执行add操作或查询前缀和,从而执行query操作。

首先,我们来介绍“lowbit”。对于一个数x,lowbit(x)是x的二进制里最右面的1所代表的数字,例如lowbit(20)=4,因为20的二进制为10100,最右面的1代表4。怎样计算lowbit呢?很简单,lowbit(x)=x&-x。这是因为负数是用补码保存的,-x就相当于(~x)+1。例(用8位计算):

20=(00010100)

-20=(11101100)

现在我们来介绍二叉索引树。二叉索引树在程序中也是用数组来保存的。

(画得难看请见谅)

图中,深灰色方块代表树中的结点(结点0为虚拟结点),灰色线段代表树中的边。这些边并不需要显式保存;对于一个节点x,若它是父结点的左子结点,则父结点编号为x-lowbit(x),否则为x+lowbit(x)。图中的每个结点左侧都有一个白色长条(包括它自己),如结点4的长条为1~4,节点3的长条为3~3。不难发现,结点x的长条为(x-lowbit(x)+1)~x。

然后,我们用一个数组S储存每个结点的白色长条内的所有数的和。例如,S4=A1+A2+A3+A4。这样,就可以使用一个辅助的前缀和数组,在O(n)时间内从左到右初始化S数组。代码如下:

 1 int n;
 2 int S[maxn],A[mxan],S1[maxn];
 3 int begin()
 4 {
 5     memset(S1,0,sizeof(S1));
 6     for(int i=1;i<=n;i++)
 7     {
 8         S1[i]=S1[i-1]+A[i];
 9         S[i]=S1[i]-S1[i-lowbit(i)];
10     }
11 }

最后让我们来实现add操作和查询操作。对于某个add操作的结点i,它的修改会影响到那些结点呢?很显然,在它下面的结点(lowbit比它小)不会受到影响,在它左边的结点也不会受到影响,所以只需要考虑在它右边且lowbit比它大的第一个结点,这个结点的白色长条一定覆盖x。很显然,这个结点的编号为i+lowbit(i)。

接下来,x结点不需要考虑了,让我们来考虑i+lowbit(i)结点。与上面一样,只需要考虑在它右边且lowbit比它大的第一个结点,所以只需要让i+=lowbit(i)。代码如下:

1 void add(int i,int x)
2 {
3     //若需要其他操作,可以加一句:
4     //A[i]+=x;
5     while(i<=n)
6     {
7         S[i]+=x;i+=lowbit(i);
8     }
9 }

查询前缀和的方法与之类似,只是i+=lowbit(i);改成了i-=lowbit(i);这里不再介绍证明方法,与上面类似。代码如下:

 1 int query2(int i)
 2 {
 3     int sum=0;
 4     while(i!=0)
 5     {
 6         sum+=S[i];i-=lowbit(i);
 7     }
 8     return sum;
 9 }
10 int query(int l,int r)
11 {
12     return query2(r)-query2(l-1);
13 }

最后,若有建议请提出。

时间: 2024-10-29 19:07:14

C++实用数据结构:二叉索引树的相关文章

二叉索引树(树状数组)入门(一)

二叉索引树,即树状数组,被某神犇称之为是最漂亮的数据结构,所以蒟蒻北篱也去学习了一下传说中的树状数组. 限于蒟蒻北篱的语言表达能力太差(其实是懒),于是引用了度娘的一段对树状数组的解释 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值:经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅

【算法#3】树状数组&amp;二叉索引树

其实是数据结构. 智推连续几天给我推树状数组的模板,还放在第一位-- 对着蓝书的图看了好几天才看懂,树状数组的另外一个名字是二叉索引树,指通过把一个数组抽象的变形成树状的以求得到树形数据结构的效果.有人说是线段树的阉割版,我不太清楚,树状数组应该是不支持区间修改加速的. 首先我们需要理解lowbit的概念,它指的是一个数转成二进制后位数最低的那个1表示的值.它具有一个特殊的性质但是为什么具有这个性质是无须证明也不用了解的. 然后我们画一个图,在一定范围内按lowbit的大小从上向下逐层分布,一层

二叉索引树BIT

定义     二叉索引树,binary index tree,又名树状数组,或Fenwick Tree,因为本算法由Fenwick创造.     对于数组A,定义Query(i,j) = Ai +Ai+1 + - + Aj.     比较好的做法:使用前缀和,Sum(j) – Sum(i-1)即可得到Query(i,j)     BIT即为解决此类区间查询而大展身手,因为预处理时间为O(n),之后的查询时间为O(1),是属于典型的在线算法(关于在线算法,通俗地可以理解为,做一次预处理,提供多次"

实用数据结构---树状数组(二叉索引树)

树状数组适用于动态连续和查询问题,就是给定一个区间, 查询某一段的和或者修改某一位置的值. 关于树状数组的结构请去百度百科,否则将看不懂下面内容 我们看这个题 士兵杀敌(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5 描述 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. 小工是南将军手下的军师,南将军经常想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧. 南将军的某次询问之后士兵i可能又杀敌q人,之后南将军再询问的时候,需要考虑到新

浅析树状数组(二叉索引树)及一些模板

树状数组 动态连续和查询问题.给定一个n个元素的数组a1.a2.……,an,设计一个数据结构,支持以下两种操作:1.add(x,d):让ax增加d;2.query(l,r):计算al+al+1+…+ar 如何让query和add都能快速完成呢?方法有很多,这里介绍的便是树状数组.为此我们先介绍lowbit. 对于正整数x,我们定义lowbit(x)为x的二进制表达式中最右边的1所对应的值(而不是这个比特的序号).比如,38288的二进制1001010110010000,所以lowbit(3828

【树状数组(二叉索引树)】轻院热身—candy、NYOJ-116士兵杀敌(二)

[概念] 转载连接:树状数组 讲的挺好. 这两题非常的相似,查询区间的累加和.更新结点.Add(x,d) 与 Query(L,R) 的操作 [题目链接:candy] 唉,也是现在才发现这题用了这个知识,当初A的第一个数据结构的题就是关于树状数组的,忘了忘了.. Problem C: candy Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 252  Solved: 63 SubmitStatusWeb Board Description Kimi

二叉索引树 树状数组

树状数组 维护一个序列 a1 a2 a3……an 支持两种操作: 1. sum(int a,int b) a~b的区间和 2. add(int x,int d) 第x个数增加d 设lowbit(x)为x的二进制最右边的1表示的值 如lowbit(38288)=lowbit(1001010110010000)=10000=16 对于节点i,如果它是左子结点,父节点为i+lowbit(i):如果它是右子节点,那么父节点是i-lowbit(i) 定义一个数组C C1 = A1 C2 = A1 + A2

二叉索引树 BIT

BIT 说白了    是根据  数的二进制所显示的特征  建成的一棵树 首先明白 lowbit(x)  表示的意思 他表示一个数 最右边的 1 所对应的值 lowbit(x)= x&-x ; 下面根据lowbit 来建树 ***********建在脑子里********* BIT特点: 首先将每一个节点 进行 编号 1.每一层 节点 的  lowbit  相等 2.一个节点 i 若是左子节点  则  它所对应的父节点的序号为  i - lowbit(i) 右                  

树状数组(二叉索引树 BIT Fenwick树) *【一维基础模板】(查询区间和+修改更新)

刘汝佳:<训练指南>Page(194) #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> using namespace std; //一维树状数组基础模板 int lowbit(int x) { return x&(-x); } int c[1001]; int sum(int x) //计算从1到x的数组元素的和 { int