飘飞的海

 

数状数组

一、概述

树状数组(binary indexed tree),能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组A中的元素可能不断地被修改,怎样才能快速地获取连续几个数的和?

二、树状数组基本操作

普通数组(共n个元素)的元素修改和连续元素求和的复杂度分别为O(1)和O(n)。树状数组通过将线性结构数组转换成伪树状结构(线性结构只能逐个扫描元素,而树状结构可以实现跳跃式扫描),使得修改和求和复杂度均为O(lgn),大大提高了整体效率。

给定序列(数列)A,我们设一个数组C满足

C[i] = A[i–2^k+ 1] + … + A[i]   //其中,k为i在二进制下末尾0的个数,i从1开始算,则我们称C为树状数组。

1、给定i,如何求2^k

答案是:2^k=i&(-i)

例如:当i=6时,C[6]=A[6-2+1]+A[6]

当我们修改A[i]的值时,可以从C[i]往根节点一路上溯,调整这条路上的所有C[]即可,这个操作的复杂度在最坏情况下就是树的高度即O(logn)。另外,对于求数列的前n项和,只需找到n以前的所有最大子树,把其根节点的C加起来即可。不难发现,这些子树的数目是n在二进制时1的个数,或者说是把n展开成2的幂方和时的项数,因此,求和操作的复杂度也是O(logn)。

树状数组能快速求任意区间的和:A[i] + A[i+1] + … + A[j],设sum(k) = A[1]+A[2]+…+A[k],则A[i] + A[i+1] + … + A[j] = sum(j)-sum(i-1)。

下面是实现代码:

    //求2^k   
    int lowbit(int t){
        return t & ( t ^ ( t - 1 ) );   
    }
    //求前n项和   
    int sum(int end){      
        int sum = 0;      
        while(end > 0){        
            sum += C[end];        
            end -= lowbit(end);     
        }     
        return sum;   
    }
    //增加某个元素的大小   
    void plus(int pos, int num){  //n是A中元素个数    
        while(pos <= n){        
            C[pos] += num;        
            pos += lowbit(pos);     
        }   
    } 

3、扩展——二维树状数组

一维树状数组很容易扩展到二维,二维树状数组如下所示:

C[x][y] = sum(A[i][j])

其中,x-lowbit[x]+1 <= i<=x且y-lowbit[y]+1 <= j <=y

4、应用

(1)    一维树状数组:

参见:http://hi.baidu.com/lilu03555/blog/item/4118f04429739580b3b7dc74.html

(2)    二维树状数组:

一个由数字构成的大矩阵,能进行两种操作

1) 对矩阵里的某个数加上一个整数(可正可负)

2) 查询某个子矩阵里所有数字的和,要求对每次查询,输出结果

posted on 2012-09-27 11:26  飘飞的海  阅读(224)  评论(0编辑  收藏  举报

导航