树状数组(二叉索引树)

树状数组(二叉索引树)

构建BIT的时间复杂度为O(NlogN),空间复杂度为O(N)。搜索和更新的复杂度为O(logN)

使用场景

  • 对于一个数组,修改其中某个元素的数值
  • 求其前n项的和
  • 求区间和

算法思路

https://www.bilibili.com/video/BV1ce411u7qP/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=a7faec7a7512b523c7bd6ba86f97edfd(算法思路参考b站链接)

给定一个数组如下:

image-20221116195154435

最简单方法就是将数组存储下来,每次求和遍历数组,但是当数据量很大,查询速度很慢。

计算前n项和,可提前对数组中元素两两求和,并记录在另一个数组,在求和阶段即可节省一半时间。

接着继续两两求和并记录,直到最后只剩一个元素

image-20221116195038312

记录结果如上图所示。观察可以发现,每一层记录的偶数数据是没有用处的,去掉也不影响结果。

对记录结果进行优化,并将结果记录到一个数组中,如下所示:

image-20221116195002214

这个数组和原始数组正好一样长,数组中的每一个元素,正好对应下面每一个区间。

image-20221116200315204

求和时,只需要找到对应的区间求和即可,

修改某个数据时,也只需要向上找到包含他的区间进行修改即可。

求lowbit

即该二进制中值为1的最低位对应的权值。

    public static int lowBit(int x){
        return x&(-x);
    }

举例:

数字 9 ,占位 8 位,即 0000 1001 , -9 = ( 1111 0111 )

9 & -9 = ( 0000 0001 ) =1 权值为1

8 & -8 = ( 0000 1000 ) =8 权值为8

结果为该数的从右往左数第一个为 1 的位的权值。

性质

image-20221116201131837

  • 每一层数组序号的最低比特位和区间长度相同 (如上图第二层 lowbit(2)=2,区间长度为2)
  • 序号为i的序列正好就是长度为lowbit(i)且以i结尾的序列
  • 一个序列bittree[i]正上方的序列,正好就是bittree[i+lowbit(i)],即(该层序号为i,它上一层节点序号为i+lowbit(i))

注意:

  • 这里求lowbit其实是在求该序号对应的数组位于哪一层,每一层的长度依次是1,2,4,8,16等,计算出lowbit的值即是改序号对应的层数

求前缀和

        for(int i = index;i>0;i=i-lowBit(i)){
            res += bittree[i];
        }

求前14项和,计算14-lowbit(14) =12 ,问题转换为求前12项的和+bittree[14];(以此类推)

修改单点值

修改单点值,修改它本身以及上层包含它的项。

该层序号为i,它上一层节点序号为i+lowbit(i);

        for(int i= index;i<=n;i=i+lowBit(i)){
            bittree[i] += increase;
        }

具体代码

public class BITDemo {
    static int[] nums;
    static int[] bittree; //注意下标是从1到n;为了保整求lowbit一致
    static int n;
//    求lowbit,计算x二进制数为1的最低位
    public static int lowBit(int x){
        return x&(-x);
    }
    // 单点更新,将新修改的值增加到父节点上
    public static void add(int index,int increase){
        for(int i= index;i<=n;i=i+lowBit(i)){
            bittree[i] += increase;
        }
    }
    //    单点更新,修改某一位置的值
    public static void update(int index,int value){
        add(index,value);
        nums[index-1] = value;
    }
    // 查询前缀和
    public static int query(int index){
        int res =0;
        for(int i = index;i>0;i=i-lowBit(i)){
            res += bittree[i];
        }
        return res;
    }
    // 查区间和
    public static int range(int left,int right){
        return query(right)-query(left);
    }

    public static void main(String[] args) {
        nums = new int[]{2,3,5,1,4,2,1};
        n = nums.length;
        bittree = new int[n+1];
//        构造bittree树
        for(int i=0;i<n;i++){
            add(i+1,nums[i]);
        }
        System.out.println(query(2));         //5
        System.out.println(query(5));           //15
        System.out.println(range(2,5));     //10
    }
}
ntln(query(2));         //5
        System.out.println(query(5));           //15
        System.out.println(range(2,5));     //10
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值