• 树状数组


    一、树状数组

    树状数组是一个优美小巧的数据结构,在很多时候可以代替线段树。一句话概括就是,凡是树状数组可以解决的问题,线段树都可以解决,反过来线段树可以解决的问题,树状数组不一定能解决。

    树状数组英文名称为Binary Index Tree,直译过来就是二进制索引树,我觉得二进制索引树更能说明其本质。树状数组的本质就是一种通过二进制位来维护一个序列前i和的数据结构。

    1、更新元素

    比如修改了a[3],则:

    P1 = x = 3, L1 = 0;

    P2 = p1+ 2(L1) = 3 + 1 = 4, L2 = 2;

    P3 = p2 + 2(L2) = 4 + 4 = 8, L3 = 3;

    ....

    2、子序列求和

    比如求sum(6)

    p1 = x = 6, L1 = 1; sum += c[6];

    p2 = p1 - 2(L1) = 6 - 2 = 4; L2 = 2; sum += c[4];

    p3 = p2 - 2(L2) = 4 - 4 = 0, return;

    二、树状数组的维护

    对于维护的序列A,定义C[i]=A[j+1]+...+A[i],其中ji的二进制表示中把最右边的1换成0的值。j的值可以通过lowbit求出,即i-lowbit(i)

    lowbit(a)2^(a的二进制表示末尾0的个数)。可以用下面式子求出:

    lowbit(a)=a&(a ^ (a-1))
    //
    lowbit(a)=a&(~a+1)
    //或根据补码性质
    lowbit(a)=a&(-a)

     修改方式如下

        void modify(int p,int delta)
        {
            while (p<=N)
            {
                C[p]+=delta;
                p+=lowbit(p);
            }
        }

    求前缀和如下

        int sum(int p)
        {
            int rs=0;
            while (p)
            {
                rs+=C[p];
                p-=lowbit(p);
            }
            return rs;
        }

    三、示例代码

    package ads;
    
    /**
     * 
     * @author loull
     * 下标index从1开始数
     */
    public class BinaryIndexTree {
    
        int[] c;
        int n;
        public BinaryIndexTree(int n) {
            this.n = n;
            c = new int[n+1];
        }
        
        int lowbit(int x) {
            return x & (x ^ (x-1));
        }
        /**
         * 更新一个元素,初始数组c都是0,所以可以用这个方法初始化c,这时候增加与更新是等价的
         * @param p    更新第p个元素,索引从1开始
         * @param d    增加的值,不是更新后的值
         */
        void update(int p, int d) {
            while (p <= n) {
                c[p] += d;
                p += lowbit(p);
            }
        }
        
        /**
         * 计算从第一个元素到第p个元素的和
         * @param p
         * @return
         */
        int sum(int p) {
            int ret = 0;
            while (p > 0) {
                ret += c[p];
                p -= lowbit(p);
            }
            return ret;
        }
        
        public static void main(String[] args) {
            int[] numbers = {1,2,3,4,5,6,7,8,9};
            BinaryIndexTree bit = new BinaryIndexTree(numbers.length);
            for (int i=0; i<numbers.length; i++) {
                bit.update(i+1, numbers[i]);
            }
            
            System.out.println(bit.sum(9));
        }
    }

    四、C++模板

    /***************************************
    *                                      *
    *    Template of Binary Indexed Tree   *
    *                                      *
    *           @author: Zyzsdy            *
    *                                      *
    ****************************************
    *   Thanks: shu_mj                     *
    *           ACM Template of Tokyo U    *
    ***************************************/
    template <class Type>
    struct binaryIndexTree {
        Type *data;
        int size_n;
        binaryIndexTree (int _size) : size_n (_size + 1) {
            data = new Type[size_n]();
        }
        ~binaryIndexTree() {
            delete [] data;
        }
        void add (int _lower, Type _delta) {
            int pos = _lower + 1;
            while (pos < size_n) {
                data[pos] += _delta;
                pos += pos & -pos;
            }
        }
        Type sum (int _lower, int _upper) {
            if (_lower > 0) {
                return sum (0, _upper) - sum (0, _lower);
            } else {
                Type result = 0;
                int pos = _upper;
                while (pos > 0) {
                    result += data[pos];
                    pos -= pos & -pos;
                }
                return result;
            }
        }
    };
    typedef binaryIndexTree<int> BIT;

    这份树状数组模板支持自动根据数据类型和数据量申请内存,单点更新和区间查询。就算不用模板,这份代码也很容易记忆。。

    下面是HDU 1166,典型的树状数组题目,直接套上面的模板就可以解了。。因此不再放出完整代码,只贴上主函数,复制这两份代码再添上头文件即可运行。

    int main(int agrc, char *agrv[]) {
        int T;
        scanf("%d",&T);for(int i=1;i<=T;i++){
            printf("Case %d:n",i);
            int n;
            scanf("%d",&n);
            BIT *ps=new BIT(n);
            for(int j=0;j<n;j++){
                int p;
                scanf("%d",&p);
                ps->updateDelta(j,p);
            }
            char buff[80];
            while(true){
                scanf("%s",buff);
                if(buff[0]=='E') break;
                int posi,shuj;
                switch(buff[0]){
                case 'Q':
                    int l,r;
                    scanf("%d%d",&l,&r);
                    printf("%dn",ps->sum(l-1,r));
                    break;
                case 'A':
                    scanf("%d%d",&posi,&shuj);
                    ps->updateDelta(posi-1,shuj);
                    break;
                case 'S':
                    scanf("%d%d",&posi,&shuj);
                    ps->updateDelta(posi-1,-shuj);
                    break;
                }
            }
            delete ps;
        }
    }
  • 相关阅读:
    Jq操作表格
    最新web 2.0 配色 (一)
    Jqtable edit
    Jq公告渐隐弹出
    Jq模拟最大化最小化关闭
    JqtoggleClass
    Jq弹出公告
    jquery左右拉效果
    最新web 2.0 配色 (二)
    ZenCoding各个工具安装
  • 原文地址:https://www.cnblogs.com/549294286/p/3783961.html
Copyright © 2020-2023  润新知