树状树组(Binary Indexed Tree (BIT))的C++部分实现

一、树状数组的用处

树状树组是将一个线性数组保存为“树状”,当修改某点的值、求某个区间的和的时候能够有效的减少时间复杂度。当数组长度为N,实时对数组进行M次修改或求和,最坏的情况下复杂度是O(M*N)。

二、树状数组的建立

假设输入数组为

vector<int> nums

将其转化为树状数组的本质在于将数组的原先顺序打乱后,经过特殊的求和方法,组合成新的数组,代码如下。关键点在于k+=k&-k,这是一个利用二进制码的特点完成树状数组下标的选取。

 1 size = nums.size();
 2 bitTree = vector<int>(size+1,0);
 3 for (int i =1;i<=size;i++)
 4 {
 5        int k=i;
 6        while (k<size+1)
 7        {
 8               bitTree[k] += nums[i-1];
 9               k += k & -k;
10         }
11  }

树状数组建立的原理:

如下图,来源自百度图片,c数组是树状数组,a数组是原先的线性数组,可以看见树状数组的元素是a数组的元素或者元素之和,其中c[8]+c[9]即是线性数组的a的总和。对于树状数组而言,其建立顺序(按下标排列)是(循环1)c[1]+=a[0];c[2]+=a[0];c[4]+=a[0];c[8]+=a[0];

                  (循环2)c[2]+=a[1];c[4]+=a[1];c[8]+=a[1];

                  (循环3)c[3]+=a[2];c[4]+=a[2];c[8]+=a[2];

                  (循环4)c[4]+=a[3];c[8]+=a[3];

                  (循环5)c[5]+=a[4];c[8]+=a[4];

                  (循环6)c[6]+=a[5];c[8]+=a[5];

                  (循环7)c[7]+=a[6];c[8]+=a[6];

                  (循环8)c[8]+=a[7];

                  (循环9)c[9]+=a[8];

可见,第一次循环加的是1,2,4,8;第二次是2,4,8;第三次是3,4,8等等。其规律是第i循环中,c[k]与a[i-1]相加,而k与i的关系是k是i二进制数保留最高位1后相加的结果,比如i=2=0010,其二进制数保留最高位1后是0010,故第1个k=0010+0010=0100=4,再对k进行处理,得第二个k=8,直至k>size+1。又比如i=3=0010,其二进制数保留最高位1后是0010,第1个k=0010+0010=0100,第2个k=0100+0100=8。之所以采用这种方法因为树状数组采用了二分的思想,比如c[8]会等于a[0]~a[3]与a[4]~a[7]两部分的和。

故建立数组的关键在于求i二进制数保留最高位1后相加的结果,其方法是:令k=i,k+=k&-k,即可求得结果。

三、树状数组的更新和部分求和

更新数组:如果此时原数组中的一个元素被改变,那么树状数组中许多值需要被更新,这因为树状数组中的元素之间存在可能的联系,这种联系与树状数组下标值相关。因此更新树状数组并不是单纯的单个值替换,代码如下:

 1     void update(int i, int val) {
 2         int ret = val-nums[i];
 3         int k=i+1;
 4         while (k<size+1)
 5         {
 6             bitTree[k]+=ret;
 7             k+=k&-k;
 8         }
 9         nums[i] = val;
10     }

数组部分求和:分别先求0到i下标的和,再求0到j+1下标的和,它们之间的差即是下标i到下标j的和,数组部分求和代码如下:

 1     int sumRange(int i, int j) {
 2         int result1 = 0,result2 = 0;
 3         int k=i;
 4         while (k)
 5         {
 6             result1+=sum[k];
 7             k-=k&-k;
 8         }
 9         k=j+1;
10         while (k)
11         {
12             result2+=sum[k];
13             k -= k&-k;
14         }
15         return result2-result1;
16     }
时间: 2024-10-18 20:03:57

树状树组(Binary Indexed Tree (BIT))的C++部分实现的相关文章

树状数组(Binary Indexed Tree,BIT)

树状数组(Binary Indexed Tree) 前面几篇文章我们分享的都是关于区间求和问题的几种解决方案,同时也介绍了线段树这样的数据结构,我们从中可以体会到合理解决方案带来的便利,对于大部分区间问题,线段树都有其绝对的优势,今天这篇文章,我们就来欣赏由线段树变形的另外一个数据结构--树状数组,树状数组通常也用于解决区间求和.单点更新的问题,而且效率比线段树高一些(树状数组区间求和和单点更新的时间复杂度均为o(log n)),相对而言,线段树的应用范围可能更广泛一些.但不得不承认,树状数组确

POJ1990 MooFest 树状数组(Binary Indexed Tree,BIT)

N头牛排成一列,每头牛的听力是Vi,每头牛的位置Pi,任意两头牛i,j相互交流时两头牛都至少需要发出声音的大小为max(Vi,Vj) * |Pi-Pj|,求这N头牛两两交流总共发出的声音大小是多少.N,V,P都是1-20000的范围. 这题首先对Vi从小到大进行排序,排序过后就可以依次计算i,将所有比Vi小的牛到i之间的距离之和乘以Vi得到Ri,然后累加Ri就是最终结果.问题是Ri具体该如何求. 假设听力比Vi小的牛并且位置也比Pi小的牛的个数为Ci,并且这些距离之和为Si,听力比Vi小的所有牛

树状数组(Binary Indexed Tree)

对于学习的线段树方面的,发现很多问题可以用它来求解,但是做题的时候发现,用线段树太容易tle也,很多次也是卡时间过的,然后就发现了树状数组. 首先我们搞明白树状数组是用来干嘛的,树状数组是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值,经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询). 所以说,树状数组的求解和线段树的求解有些重合,但是功

HDU 5877 dfs+ 线段树(或+树状树组)

1.HDU 5877  Weak Pair 2.总结:有多种做法,这里写了dfs+线段树(或+树状树组),还可用主席树或平衡树,但还不会这两个 3.思路:利用dfs遍历子节点,同时对于每个子节点au,查询它有多少个祖先av满足av<=k/au. (1)dfs+线段树 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm>

SRM 627 D1L2GraphInversionsDFS查找指定长度的所有路径 Binary indexed tree (BIT)

题目:http://community.topcoder.com/stat?c=problem_statement&pm=13275&rd=16008 由于图中边数不多,选择DFS遍历全部路径,计算路径Inversions时使用了一个R[] 数组,能够在O(N)时间内得到路径Inversions,又由于该图所以路径条数为O(N^2),算法复杂度为O(N^3),对于N为1000的限制来说,复杂度较高,但实际測试中,最慢的測试用例费时700多ms,没有超时.若要减小复杂度,须要更高效的算法来计

Binary Indexed Tree (Fenwick Tree)

Binary Indexed Tree 空间复杂度O(N),查询时间复杂度O(lgN). 其中每个元素,存储的是数组中一段(注意起始元素看作1而非0)的和: 假设这个元素下标为i,找到i的最低位1,从最低位1开始的低部表示的是长度,去除最低位1剩下的部分加上1表示的是起始位置,例如: 8二进制表示为100 最低位1也是最高位,从1开始的低部也即100本身,因此长度为8. 去除1以后,剩下为0,加上1以后为1.所以sum[8]表示的是从第1个元素开始的8个元素的和. 又比如11的二进制表示为101

Hdu5921 Binary Indexed Tree

Hdu5921 Binary Indexed Tree 思路 计数问题,题目重点在于二进制下1的次数的统计,很多题解用了数位DP来辅助计算,定义g(i)表示i的二进制中1的个数, $ans = \sum_{i=1}^n \sum_{j=0}^{i-1} g(i,j) = 0.5\sum_{i=0}^n\sum_{j=0}^n[g(i)+g(j)-2g(lcp(i,j))] $ 即先计算每个位的贡献,再减去重复的地方. 先计算前者,每个数会出现n+1 次,所以结果乘以n+1 即可,对第i位,统计这

Binary Indexed Tree (树状数组)

树状数组是能够完成下述操作的数据结构 给一个初始值全为0的数列a1,a2,-an. *给定i,计算a1+a2+-+ai *给定i和x,执行ai += x 1.基于线段树的实现 如果使用线段树,只需要对RMQ的样例做少许修改就可以实现这两个功能.线段树的每个节点上维护的是对应的区间的和. 接下来,我们来看如何计算从s到t的和.在基于线段树的实现中,这个和是可以直接求得的. 但是如果我们能够计算(从1到t的和)-(从1到s-1的和),同样可以得到s到t的和.也就是说,只要对于任意i,我们都能计算出1

树状下拉选择框tree:comboTree 单选

  ------------------------------------------------------------------------HTML--------------------------------------------------------------------- <tree:comboTree displayValue="--无上级行政区--" value="-1" treedata="${dataLst}"