学弟讲算法-树状数组

一、概念

蓝色A[i]表示输入的数组;绿色C[i]表示对应的树状数组

定义C[i]的值为它所有子结点值和A[i]的总和,存在:

  • C[1] = A[1]
  • C[2] = C[1] + A[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] = C[5] + A[6] = A[5] + A[6]
  • C[7] = A[7]
  • C[8] = C[4] + C[6] + C[7] + A[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8]

结论:

  • C[i]中一定包含A[i]的值
  • i为奇数时: C[i] = A[i]
  • C[i]表示数组A的一段连续区间和

二、性质

1. 区间范围

C[i]表示数组A的一段连续区间和,而右区间一定是i,即C[i]管辖区间的末尾元素是A[i]

下标为i的C[i]所管辖元素的个数为 2 k 2^{k} 个(k为i的二进制中最低位到最高位连续0的长度)

例:

  • C[8] : ( 8 ) 10 (8)_{10} = ( 1000 ) 2 (1000)_{2} ,最低位到最高位连续0的长度为3,故k=3,管辖数有 2 3 2^{3} 个,C[8]是8个数(A[1]-A[8])的和


  • C[5] : ( 5 ) 10 (5)_{10} = ( 0101 ) 2 (0101)_{2} ,最低位到最高位连续0的长度为0,故k=0,管辖数有 2 0 2^{0} 个,C[5]是1个数(A[5])的和


  • C[4] : ( 4 ) 10 (4)_{10} = ( 0100 ) 2 (0100)_{2} ,最低位到最高位连续0的长度为2,故k=2,管辖数有 2 2 2^{2} 个,C[4]是4个数(A[1]-A[4])的和

现在的问题则是:给定i,如何求出 2 k 2^{k} ,前辈的经验是: 2 k 2^{k} = i&(-i),这里暂时不讨论是如何得出结果的

实现代码

//根据C[i]的下标i获取管辖范围
int lowbit(int i){
    return i&(-i);
}

2. 更新

若更新A[i]的值,则需更新C[i]与C[i]祖先节点的值

例:

  • 更新A[1] : 则需更新所有包含A[1]的C[i],即C[1]和其祖先节点C[2],C[4],C[8]
    过程:
  • i = 1; C[1]+=A[1]
  • lowbit(1) = 1; 1+lowbit(1) = 2 ; C[2]+=A[1]
  • lowbit(2) = 2; 2+lowbit(2) = 4 ; C[4]+=A[1]
  • lowbit(4) = 4; 4+lowbit(4) = 8 ; C[8]+=A[1]

  • 更新A[6] : 则需更新所有包含A[6]的C[i],即C[6]和其祖先节点C[8]
    过程:
  • i = 6; C[6]+=A[6]
  • lowbit(6) = 2; 6+lowbit(6) = 8 ; C[8]+=A[6]

实现代码

/**
*param : x,更新位置
*param : v,增加或减少值
*param : n,数组长度
**/
void update(int x,int v,int n){
    a[x] +=v;
    for(int i = x; i<=n; i+=lowbit(i)){
        c[i] += v;
    }
}

3. 区间和

//求A[1]-A[X]的和
int getSum(int x){
    int sum = 0;
    for(int i = x; i > 0 ;i-=lowbit(i)){
        sum += c[i];
    }
    return sum;
}

猜你喜欢

转载自blog.csdn.net/qq_41452937/article/details/107405159
今日推荐