Leetcode 307. 区域和检索 - 数组可修改 树状数组 Java

树状数组

图片1.png

C[i]代表子树的叶子节点的权值之和:
C[1]=A[1];
C[2]=A[1]+A[2];
C[3]=A[3];
C[4]=A[1]+A[2]+A[3]+A[4];
C[5]=A[5];
C[6]=A[5]+A[6];
C[7]=A[7];
C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

将C数组的下标i转化成二进制:
1=(001) C[1]=A[1];
2=(010) C[2]=A[1]+A[2];
3=(011) C[3]=A[3];
4=(100) C[4]=A[1]+A[2]+A[3]+A[4];
5=(101) C[5]=A[5];
6=(110) C[6]=A[5]+A[6];
7=(111) C[7]=A[7];
8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8];

项数 等于 二进制最右边(最低位)的1表示的整数

lowbit

int lowbit(x) 
{   return x & -x;   }

补码的另一种求法是 找到最右边的1 把这个数字左边的全部数字取反

前缀和

int sum(int i)         //求区间[1,i]所有元素的和
{   int ret=0;
    while(i>0){
        ret+=C[i];     //从右往左区间求和
        i-=lowbit(i);
    }
    return ret;            
}

已经解决的问题:
在构造出树状数组C[i]的前提下,求前缀和

如何进行区间查询?
sum® - sum(L-1)

下一个问题:单点更新

单点更新 更新后缀和

void update(int i,int val)//单点更新(影响多个C元素)
{   while(i<=n){
        C[i]+=val;
        i+=lowbit(i);     //由叶子节点向上更新C数组
    }
}  
//更新(从小到大)是查询(从大到小)的逆过程

代码

class NumArray {
    int[] c;
    int[] a;
    int len;
    public NumArray(int[] nums) {
        a = new int[nums.length+1];
        System.arraycopy(nums, 0, a, 1, nums.length);
        len = nums.length+1;
        c = new int[len];
        for(int i = 1; i<len; i++){
            initialize(i, a[i]);
        }
    }
    private void initialize(int i, int val){
        while(i<len){
            c[i] += val;
            i += lowbit(i);
        }
    }

    public void update(int i, int val) {
        i++;
        int delta = val - a[i];
        a[i] = val;
        while(i<len){
            c[i] += delta;
            i += lowbit(i);
        }
    }

    private int lowbit(int x) {
        return x&(-x);
    }

    public int sumRange(int i, int j) {
        int sumi = getSum(i);
        int sumj = getSum(j+1);
        return sumj - sumi;
    }

    private int getSum(int i) {
        int res = 0;
        while(i > 0){
            res += c[i];
            i -= lowbit(i);
        }
        return res;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值