树状数组(BIT)初学

  BIT(Binary Indexed Tree,BIT) 树状数组。树状数组是一类怎样的数据结构呢?我们知道,树状数组是用来解决动态连续和的查询问题而诞生的。

数据结构就是说,给你n个元素的数组a[1],a[2],a[3]....a[n](下标要从1开始)然后,支持以下两种操作:

    1.Add(x,y)就是说对下标为x的a[x]加d。模板默认为update(x,y)

    2.Query(L,R)就是计算a[L]+a[L+1]+...+a[R]。模板默认为read(x)求出1-x的和,然后read(y)求出1-y的和,然后再用read(y)-read(x-1)得到[x,y]的和

所以说,BIT是一种动态的连续和查询问题。

  在学习BIT之前,我们必须对于位运算有一定的认识和了解,我们知道,对于负数来说,在计算机中是以其补码的形式存放的,而补码就是在其原码的基础上,符号位不能发生变化,其余各位取反后加一得到的结果。

  看看这样的运算,比如说7的二进制表示:00000111,那么-7的二进制表示就是:1111001,那么 00000111&11110001的结果是00000001,所以

这样的运算帮我们找到了7 的二进制表示中,最后一个1的位置,有了这个概念。我们如果进行j-=j&(-j)的运算,就知道了这是在把111->110->100->000的过程了。

说了这么多了,那就来说说树状数组到底是怎么实现的呢?用tree[].    tree[i]表示的是[i-(i&(-i))+1,i]这个 区间内a数组元素的和 (为什么会是这个,下文会有解释)

比如我们for( int i = 1;i <= n;i++ )a[i]=i;

想要求出a[1]~a[15]的元素的值。我们知道15的二进制表示是(1111)2

tree[15] = sum[15,15];

tree[14] = sum[13,14];

tree[12] = sum[9,12];

tree[8] = sum[1,8]; 

-> sum[1,15] = tree[1,8]+tree[9,12]+tree[13,14]+tree[15,15];

  为什么会是这样的结果,我们发现,对于结点i来说,如果他是左子结点的话,那么父亲的节点编号就是i+i&(-i)。

如果i是右子结点的话,那么父亲节点的编号就是i-i&(-i);  

不难证明,这两个操作的时间复杂度都是O(nlogn),预处理的时候,先把A数组和C数组清空,然后执行n次update()操作,总的时间复杂度是nlogn。

-read( int pos )求出sum[1,pos]。

-update( int pos,int v ) 把a[pos]加v

需要注意的是:更新点然后查询区间,下标必须从1开始。

如果要是计算sum[l,r]的值,那我们就用read(l)-read(r-1)来实现

下面来介绍read的两种实现方式.

第一种,就是用while写的

int read ( int pos )

{

    int ans = 0;

    while ( pos>0 )

    {

        ans+=tree[pos];

        pos-=pos&(-pos);

    }

    return ans;

}

第二种,使用for写的

int read ( int pos )

{

    int ans = 0;

    for ( int j = pos;j;j-=j&(-j) )

    {

        ans+=tree[j];

    }

}

 

再来就是update( int pos,int val )的写法了

第一种,while写法

void update( int pos,int val )

{

    while ( pos <= n )

    {

        tree[pos]+=val;

        pos+=pos&(-pos);

    }

}

第二种,for写法

void update( int pos,int val )

{

    for ( int j = pos;j <= n;j+=j&(-j) )

    {

        tree[j]+=val;

    }

}

 

根据自己的习惯来写吧,其实都不还可以。

 

 

 

 

数据结构学习路线:

树状数组->堆->线段树->平衡树->可并堆->持久化线段树->树套树(->仙人掌)

你可能感兴趣的:(树状数组)