zoukankan      html  css  js  c++  java
  • 树状数组深度剖析

    前言


    树状数组最开始学的时候因为整不明白,所以就只是知道怎么用,网上很多人对着结论口胡树状数组的原理,印象不是特别深刻。后来重新学习了一遍,看了下知乎关于树状数组的讨论,希望能深入了解树状数组的原理,但是真的有点难理解,而且说法和思想有好几种,有的地方有可能还不够严谨,希望看完能对你有所启发

    引入


    假设我们有个一维数组,要进行两种操作:

    1.修改某个元素

    2.求指定区间的和

    对于这个问题,我们有两种简单的解法

    • 修改元素$i$直接通过下标访问($O(1)$),求区间和就按照区间进行累加($O(n)$)
    • 先进行前缀和预处理,$sum[r]-sum[l-1]$得到区间和($O(1)$),但是修改元素$i$,就要把$i$之前的前缀和都更新($O(n)$)

    可以看出两种方法各有优劣,一类折中的思路是同样存储一些区间内的计算结果,节省求和的时间,但尽量减少重叠区间的数量,从而减少更新的次数。

    原理


     如何高效的统计和修改数组内的信息呢?我们可以想到用树形结构

    如果只需要查询前缀和的话,在线段树上查询是不需要用到右儿子的值的(如果要用,那么左儿子的值会被用到,那么还不如用父亲节点的值),所以去掉所有右儿子,就得到了树状数组的结构,如图:

     而我们可以把这里的$lowbit$理解为树的高度。观察下可以发现对于位置 $i$,其对应的结点所在的高度就是 $lowbit(i) $的位数,第一层是所有$lowbit(i)=1$的节点,第二层是所有$lowbit(i)=2$的节点······

    节点的高度又决定了其子树的大小,因此对于节点$i$,它所维护的信息区间为$(i-lowbit(i),i]$

    对于区间查询,我们是用右边界前缀和减去左边界前缀和得到。

    由于任何正整数都能表示为2的幂相加的形式,那么求前缀和可以由多个长度为2的幂的区间的和得到。比如$19=2^{4}+2^{1}+2^{0}$,就是由前16个元素的和,再往后两个元素的和,再往后一个元素相加得到。那么我们可以不断去掉二进制末尾的1,统计对应区间的信息进行相加就可以了。

    至于单点修改,理解起来可能稍微难懂一点(个人观点)

    由于树形结构维护区间信息,那从这个节点往父节点走一直走到根节点,路径上的节点都要修改。关键在于如何找到父节点,其实对于当前节点$i$,它下一个存在的父节点就是$i+lowbit(i)$,为什么呢?

    留坑吧,发现之前的证明存在问题

     实现


    了解了树状数组的原理后,想必实现起来思路就会清晰很多

    int lowbit(int x){
        return x&(-x);
    } 
    //区间求和 
    int sum(int x){
        int ret = 0
        while(i){
            ret += c[x];
            x -= lowbit(x); 
        }
        return ret;
    } 
    //单点修改 
    void update(int x, int val){
        while(x<=n){
            c[x] += val;
            x += lowbit(x); //向上更新 
        }
    } 

    这里解释下$lowbit(i)$为什么可以用$i&(-i)$表示,计算机中的负数用对应正数的补码(正数取反加一)来表示

    假设这里的$i$我表示成xxxxx100,那么$-i$就是#####011+1 = #####100(这里的#表示对x取反)

    那么(xxxxx100)&(#####100) = 00000100,就是$i$的二进制的最低位1对应的值


    Reference:

    https://www.zhihu.com/question/54404092

    https://www.cnblogs.com/acgoto/p/8583952.html

  • 相关阅读:
    人月神话读书笔记
    读人月神话有感
    Codeforces 137D
    Codeforces 1138B
    <WFU暑假训练一> 解题报告
    Codeforces 1250B
    Codeforces 1038D
    Codeforces 1202D
    Codeforces 87B
    Codeforces 208C
  • 原文地址:https://www.cnblogs.com/wizarderror/p/12308175.html
Copyright © 2011-2022 走看看