树状数组学习

一、什么是树状数组?

在这里插入图片描述

二、树状数组的两个基本操作

在这里插入图片描述

三、树状数组的特点

  1. 树状数组用于求解给定数组中任意区间的累计频率(前缀和、区间和)问题,支持单点更新,不支持增加和删除元素。
  2. 树状数组的本质是一个数组,根据元素的索引建立起“逻辑上的”树形结构,元素间便有了联系,所以也叫二叉索引树。这里的“索引”可以理解为树形结构就是一种索引结构,也可以理解为,树状数组是基于元素的索引建立关系的。

四、代码实现

/**
 *
 * <p>树状数组或二叉索引树(英语:Binary Indexed Tree),以其发明者命名为Fenwick树。
 * 其初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,
 * 现多用于高效计算数列的前缀和, 区间和。</p>
 *
 * <p>虽然BIT的名字是“二叉索引树”,实际上我们并没有使用一个“树”的结构去存储BIT对象,
 * 而是将其放在一个数组中,所以这种结构被称为“树状数组”。
 * 许多树结构也使用了这样的形式,比如二叉堆。</p>
 *
 * @author edu.lagou.com
 */
public class BinaryIndexedTree {
    int[] BIT;
    int len;

    /**
     * 初始化BIT。
     * 需要先调用{@link #update(int, int)}填充数据之后,
     * 才能使用{@link #getSum(int)}方法
     *
     * @param length 数组长度
     */
    public BinaryIndexedTree(int length) {
        len = length;
        // 将BIT数据存储到索引[1, len]上
        BIT = new int[len + 1];
    }

    /**
     * 初始化BIT。
     * 默认操作:将给定数组元素的前缀和填充到BIT中。
     * 可根据需要重写{@link #init(int[])}方法来改变此行为。
     *
     * @param nums 给定数组
     */
    public BinaryIndexedTree(int[] nums) {
        this(nums.length);
        init(nums);
    }

    /**
     * 记录给定数组的前缀和。
     * 子类根据实际业务重写此方法。
     */
    public void init(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            update(i, arr[i]);
        }
    }

    public int update(int i, int k) {
        // 将数据存储到加1位置上
        i = i + 1;
        // 更新节点个数
        int c = 0;

        while (i <= len) {
            BIT[i] += k;
            i += lowBit(i);
            c++;
        }
        return c;
    }

    public int getSum(int i) {
        // 从i加1位置上取数据
        i = i + 1;
        int res = 0;

        while (i > 0) {
            res += BIT[i];
            // 定位下一个关键节点
            i -= lowBit(i);
        }
        return res;
    }

    /**
     * 获取给定整数的最低位 1 所代表的值。
     * 比如:
     *  整数1:返回二进制表示(0000 0001)中最后的 1 的十进制值 1。
     *  整数10:返回二进制表示(0000 1010)中最后的 1 的十进制值 2。
     * 运算过程如下:
     *      1 & -1 = 1
     *      0000 0001
     *    & 1111 1111
     *    = 0000 0001
     *
     *      10 & -10 = 2
     *      0000 1010
     *    & 1111 0110
     *    = 0000 0010
     * @param x
     * @return
     */
    public int lowBit(int x) {
        // 等价于:x & (~x + 1)
        return x & (-x);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值