树状数组基础

来源:互联网 发布:linux mysqldump 编辑:程序博客网 时间:2024/06/08 05:21

与其说是写博贴,不如说是做个总结。几个月前接触了树状数组,首先的感觉是好神奇啊!用那么简单而优美的几行代码就实现了那么强大的功能!mogicmagic啊!一直都很崇拜发明树状树组的人。可惜后来因为被抓去刷文化课去了,就一直没有彻底搞懂树状数组。这几天放假,终于把这个优美的玩意搞清楚了,在这里做个总结。好了,废话少说,开始吧。


一、树状数组是什么?能干啥?能吃吗?

首先,不能吃!

树状数组(Binary Indexed Tree(BIT), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。              百度百科


说人话

 平常我们会遇到一些对数组进行维护查询的操作,比如,修改某点的值、求某个区间的和。一般的暴力,修改的复杂度是O(1),查询的复杂度是O(N)。如果在线的对数组进行M次修改或求和,最坏的情况下复杂度是O(M*N),当规模增大后肯定会爆炸!而树状数组干同样的事复杂度却是O(M*lgN)。厉害啊!


二、树状数组如何实现?

我们先来看张图。这张图是已经用烂了的,也不存在雷不雷同了。
c就是我们为a数组构建的树状数组。来看看规律。容易看出,c[8]表示a[1]~a[8]的和,c[4]表示a[1]~a[4]的和,c[2]表示a[1]~a[2]的和......是不是有种二分的感觉?那树状树组到底是怎么做到的呢?
我们从现在开始,所有的数都用二进制来表示。我们先引入一个概念:lowbit。一个数k的lowbit值就是这个数去掉所有高位的1,只留下最低位的1。比如,0110的lowbit就是0010,0100的lowbit就是0100,0011的lowbit就是0001。lowbit(k)是怎么求的呢?非常简单,就是把k与上-k,即lowbit(k) = k & -k。比如k = 1010,那么-k就是把k取反加1(这是计算机中的补码),即0101 + 1 = 0110,那么k & -k就是1。为什么呢?因为取反后,k的每一位都与取反的k不一样,这时再把取反的k加上1,即-k,那么就能保留下最后一位的1。如果还不明白的话,读者可以自己举几个例子试一试。

好,神奇的地方出现了!我们发现,设c[i]父亲节点为c[j], 那么j = i + lowbit(i)!!比如0001 + 0001 = 0010,0011 + 0001 = 0100,0110 + 0010 = 1000!请记住这个神奇而重要的性质!它在下面的算法中非常重要!至于这个性质的数学证明,请读者自行完成。

那么我们容易发现,如果要更新c[0001],那么我们也要更新c[0010]、c[0100]、c[1000],即它的所有祖先。那么可知,如果要更新c[k],那么要接着更新c[k+lowbit(k)]以及其它祖先。还不懂?上代码!
//第k个数增加x  O(lgn)void updata(int k, int x) {    while(k <= n) {            c[k] += x;            k += lowbit(k);    }}
我们已经会更新了,那查询呢?很简单,把上面的更新直接逆过来写。不多说,上代码!
//求1 ~k的和 O(lgn)int query(int k) {    int ans = 0;    while(k) {        ans += c[k];        k -= lowbit(k);    }    return ans;}
那么,求a~b的和,就直接query(b) - query(a-1)就行了。

附上完整代码
#include<iostream> #include<cstdio>using namespace std;#define N 100001int n;int c[N];int lowbit(int k) {return k & -k;}void updata(int k, int num) {while(k <= n) {c[k] += num;k += lowbit(k);}}int query(int k) {int ans = 0;while(k) {ans += c[k];k -= lowbit(k);}return ans;}int main() {cin >> n;for(int i = 1; i <= n; i++) {int a;scanf("%d", &a);updata(i, a); }int m;cin >> m;while(m--) {int op;int x, y;scanf("%d%d%d", &op, &x, &y);if(op == 1)updata(x, y);elseprintf("%d\n", query(y) - query(x-1));}return 0;} 



1 0
原创粉丝点击