树状数组

来源:互联网 发布:中国外来物种数据库 编辑:程序博客网 时间:2024/05/29 08:18


                                                         树状数组

树状数组: 也叫做二分索引数, 主要采用二分的思想,这种方式的提出,主要是因为 任何整数都可以写成 2的幂的                      和的形式 ,因此 形成了一串序列可以写成一系列子串的和
             1、例如: 元数据 a[] , 树状数组c[]  c[n] 表示的段是 c[n] = a[n-2^k +1 ] + a[n-2^k] + ......+ a[n] 
                               注意 k 表示的是二进制 下末尾 0 的个数.....
                              要思考的问题是如何 根据 n  求出2^ k 的值 

                    如图所示:
                                                 
                                          c[1] = a[1]  ;
                                          c[2] = a[1] + a[2] ;
                                          c[3] = a[3] ;
                                          c[4] = c[2] + c[3] + a[4] = a[1] +a[2] +a[3] + a[4] ;
                                          c[5] = a[5];
                                          c[6] = a[5] + a[6]  ;
                                          。。。。。。
1)  上面继续想问题 k 表示的是  n 在二进制下0 的个数   例如 6 (110) 结果是 1  输出 2^k  = 2 用什么方法来得到呢?
             2^k = n and (n xor (n-1))  
        这个公式不难推导  先验证一下正确性  110 and(110 xor 101) = 110 and 011 = 010 = 2D  
结果是对的.. 至于怎么来的其实我也不知道  也可以写成 2 ^k = x and(-x)  -x 表示的是x 的补码 即 取反加1 
代码:
int lowbit(int x){//函数功能主要是根据n 求2^k 方便动态维护和查询更新    return x&(-x);}

2) 要想的问题就是 更新维护操作了(下面讨论一维的)
这里解释为什么 要有更新操作 当原数组在pos位置 加入一个数w , 又因为数组数组c[n ] 表示的是一段数组
要更新的东西不仅仅是一个了,是很多个  而且是向上更新 从包含x 的开始向上更新
void update1(int pos, int w){     //这里讨论的是一维的     /*也可以写成      while(pos<=n)      {          c[pos] += w;          pos+= lowbit(pos);      }     */     int i;     for(i = pos;i <= n ; i+=lowbit(i))     {         c[pos] += w;     }}

下面是二维数组的更新和维护函数(其实如果再加上循环的话就可以建立一个树状数组了
                   
void update2(int x, int y,int w){//这里是二维的树状数组的更新方便查询和维护    int i , j;    for(i = x ; i <= n ; i +=lobit(i))    {        for(j = y ; j <= n ;j+=lowbit(j))        {            c[i][j] += w;        }    }}

3)下面要讨论的就是求和操作了 其实树状数组的产生也是为了降低复杂度而生成的 求和操作也可以说成是查询
这里要注意的是 n = n- lowbit(n) 的意思 这个意思是 将N 转换成二进制后最后的那个1 去掉
       例如: 上面 6 110 -lowbit(6) 即 110 - 010 得到100  6-2 = 4 
                 对于N 其实最多有logN个1   这个很容易理解  因此查询效率可以降低到log(N)  
       
先给出二维树状数组求和操作的代码:
例如要求解区间[ a,  b] 之间的和 要解决的问题是 求解sum(a, b) = sum (1, b) - sum( 1, a-1) 因此可以方便求解了
int sum(int x, int y){    /*也可以写成    int sum = 0;    int tmp;    while( x > 0 )    {       tmp = y;       while(tmp >0)       {          sum += c[i][tmp] ;          tmp -= lowbit(tmp);       }       i -= lowbit(i);    }    return sum;    */    int sum = 0;    int i , j;    for(i= x ; i > 0 ; i -=lowbit(i))    {        for(j = y ; j > 0 ; j -= lowbit(j))        {            sum += c[i][j];        }    }    return sum;}
其次给出一维树状数组的求和代码:
int sum1(int en){//这个主要是一维树状数组的求和问题了    /*也可以写成     int sum = 0;     int tmp = en;        while(tmp > 0)        {            sum += c[tmp] ;            tmp -= lowbit(tmp);        }        return sum ;    */     int j ;     int sum = 0     for(j = en ; j > 0 ;j-= lowbit(j))     {         sum  += c[j];     }     return sum;}

下面要求解的问题就是复杂度的问题了 
 树状数组能有效的改善求和问题的复杂度,但是也有他一定的局限性。。
先说明一下局限性:1)要对一个区间内进行更新 这个时候树状数组就不能有效的进行操作了
                                 2) 对于不能有效的利用 sum(a, b) = sum(1, b) - sum(1, a-1) 进行操作的也是不可以的
下面就开始说明复杂度问题了:
                  更行操作 每次都市lowbit(N) 一下因此复杂度可以说是O(log(n))  
                  求和操作或者说是查询操作复杂度也是 O(log(n)) 
            以上每次都市减掉或者加上最低位1  复杂度可以降低到 logn        

以上差不多就是树状数组的操作了,以后会补上各种题目进行练习
下面给出木模板 :
#include<iostream>using namespace std;int lowbit(int x){//函数功能主要是根据n 求2^k 方便动态维护和查询更新    return x&(-x);}void update1(int pos, int w){     //这里讨论的是一维的     /*也可以写成      while(pos>0)      {          c[pos] += num;          pos-= lowbit(pos);      }     */     int i;     for(i = pos;i > 0 ; i-=lowbit(i))     {         c[pos] += w;     }}void update2(int x, int y,int w){//这里是二维的树状数组的更新方便查询和维护    int i , j;    for(i = x ; i <= n ; i +=lobit(i))    {        for(j = y ; j <= n ;j+=lowbit(j))        {            c[i][j] += w;        }    }}int sum1(int en){//这个主要是一维树状数组的求和问题了    /*也可以写成     int sum = 0;     int tmp = en;        while(tmp > 0)        {            sum += c[tmp] ;            tmp -= lowbit(tmp);        }        return sum ;    */     int j ;     int sum = 0     for(j = en ; j > 0 ;j-= lowbit(j))     {         sum  += c[j];     }     return sum;}//下面的代码查询a[1][1] ~ a[i][j] 之间的和int sum2(int x, int y){    /*也可以写成    int sum = 0;    int tmp;    while( x > 0 )    {       tmp = y;       while(tmp >0)       {          sum += c[i][tmp] ;          tmp -= lowbit(tmp);       }       i -= lowbit(i);    }    return sum;    */    int sum = 0;    int i , j;    for(i= x ; i > 0 ; i -=lowbit(i))    {        for(j = y ; j > 0 ; j -= lowbit(j))        {            sum += c[i][j];        }    }    return sum;}int main(){    return 0;}


  
 
 


                
               

0 0
原创粉丝点击