树状数组讲解

现在我们有一个数组,我们需要为这个数组写两个函数。(且需要处理上百万的数据)

  •  修改数组中某一个元素的值
  • 求出前 n 个元素的和

我们很容易想到使用暴力遍历 [1, n] 的元素并求和写出。

本文到此结束。

但是太暴力了。在百万的数据量面前显得太不合适了。

有人可能想着这样去优化:

把数据两两求和,保存在另一个数组里面,用空间换时间。如果求某 n 个元素的和,我们就可以把时间优化为 n/2 ,节省一半的时间。而且在修改某一个元素时,也只需要多修改一个数字而已。

但是这样子还是很慢。我们可以多建几层,将元素两两求和。一直计算直至只剩下一个元素为止 ,线段树由此而来

但是我们计算时发现,在这一棵树里面,还有很多的数据没有用上,比如👇

 这些数据在计算时,完全不会使用到,因为有比他们更加好的选择,比如在计算 n = 7 时,只需要将红色圈圈里面的值相加即可得到。

 发现一些没有用的数据之后,我们将这些没用的数据去除,得到了以下结果。

通过观察我们发现,剩下的数据个数刚好为 n 个,也就是最开始的数组大小,这  就是一棵树状数组。

上面数组的每一个元素都代表着一个底下的区间,而下面每一个区间都代表着一段区间的和。求和时,我们只需要找到对应的区间,将区间求和即可

求和之前已经展示过 n = 7 的求和了。而在修改时,需要将上层的每一个区间修改。比如修改 n = 6这个元素。

接下来是Java代码。

看上去很复杂吧,需要每次都找到对应的区间,感觉找到这些规律都复杂的不得了,但是代码如下👇

    private static int bitlow(int x) {
        return x & -x;
    }

没错,这就是找到对应区间的代码。

// lowBit函数会求出一个二进制数字的最低位代表哪个数字
// 如 100010  这个二进制数的 lowBit -> 2,因为二进制最后的 1 在倒数第二位
static int lowbit(int x) {
    return x & -x;
}

这样去计算,就发现

 lowbit 值刚好等于区间对应的长度,为我们修改值时找上面一个区间提供了非常便捷的方法,只要加上 Lowbit 的值,就可以直接找到上面的区间对应的下标。在求和时,也只需要减去 lowbit 值就可以找到需要添加的区间对应的下标。

完整代码

public class Main {

    static long[] b;
    static int N;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        N = sc.nextInt();
        int m = sc.nextInt(), x, y, t;
        b = new long[N + 1];
        for (int i = 1; i <= N; i++)
            add(i, sc.nextInt());
        for (int i = 0; i < m; i++) {
            t = sc.nextInt();
            x = sc.nextInt();
            y = sc.nextInt();
            if (t == 1)
                add(x, y);
            else
                System.out.println(count(y) - count(x - 1));
        }
    }

    // lowBit函数会求出一个二进制数字的最低位代表哪个数字
    // 如 100010  这个二进制数的 lowBit -> 2,因为二进制最后的 1 在倒数第二位
    static int lowbit(int x) {
        return x & -x;
    }

    static void add(int p, int k) {
        while (p <= N) {
            b[p] += k;
            p += lowbit(p);
        }
    }

    static long count(int p) {
        long result = 0;
        while (p > 0) {
            result += b[p];
            p -= lowbit(p);
        }
        return result;
    }
}

看到这里还不懂我推荐我当时看的视频,真的超级丝滑👇

【五分钟丝滑动画讲解 | 树状数组】 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值