树状数组


                                                         树状数组

树状数组: 也叫做二分索引数, 主要采用二分的思想,这种方式的提出,主要是因为 任何整数都可以写成 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;
}


  
 
 


                
               

wangxiaoming CSDN认证博客专家 架构 Spring Boot Redis
博客是很好的总结和记录工具,如果有问题,来不及回复,关注微信公众号:程序员开发者社区,获取我的联系方式,向我提问,也可以给我发送邮件,联系 1275801617@qq.com
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付 19.89元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值