您的位置:首页 > 理论基础 > 数据结构算法

数据结构之树状数组

2018-02-26 21:52 411 查看
     树状数组(Binary Indexed Tree BIT)是一种特殊的数据结构,这种数据结构专门用来解决两种问题:给定一个数组array[ ]={a1,a2,a3...aN}(1)给定i,计算前i个数之和。(2)给定i和num,计算array[i]+num之后,对整个树状数组进行改变。这两种操作的时间复杂度在树状数组内均为(log(n))。
    省去过多的长篇大论的理论介绍,通过一个简单的例子来对树状数组进行一个初步的了解和认识。假设有一个数组a[]={1,2,3,4},现将其以两个一组作为子节点,求得二者之和作为其父节点,重复该操作,直到最终只剩下一个父节点,可以得到以下该树:
                              


那么现在看着所构造出来的这棵树来想一个问题,怎样求数组中下标从i到j的和?其实我们可以换一个角度来思考求i到j的和可以转化为((1到j的和)-(1到i-1的和))。仔细观察上面的树,不难发现在求解1到x的和过程中灰色部分的节点均不需要使用,因为不论在求和或修改树状数组两种操作中要使用这些右节点,那么其兄弟节点一定会被用到,这样的话直接使用其父节点即可(例如,求1到3的和。只需要将自上而下第二层的值为3的节点与第三层值为3的节点相加即可)。那么我们将这些冗余的节点删去,并且加以编号就可以得到一个树状数组,如下图所示:
                             


给各节点编号也有一种特殊的规律:节点编号二进制数为1结尾的对应原数组a中的区间长度为1(例如1(0001)和3(0011)对应的就是a中a[1]和a[3]的值),节点编号二进制数结尾只有连续一个0的对应原数组a中的区间长度为2(例如2(0010)对应a中a[1]和a[2]的和),节点编号二进制数结尾连续两个0的对应原数组a中的区间长度为4(例如4(0100)对应a中a[1]到a[4]的和)。
    在对树状数组进行了一定的了解之后,接下来就正式的去解决问题。
    (1)求前i项的和
     将树状数组下标i的值加到结果中,并且将i的二进制数的最小位的1减去(用i&(-i)完成该操作),重复此操作直到i变成0为止。
     以上述a数组举例,求前3项的和,先将树状数组下标为3的值加入结果中,此时结果为3,之后将3的二进制数0011的最后一个1减去,i变为2(0010),重复上面操作,此时结果为6,再将2的二进制数0010的最后一个1减去,此时i变为0,而结果也为最终结果6.实例代码如下:
//用于找出num的二进制数的最后一个1
private int dealBit(int num)
{
return num&(-num);
}
//用于求前n个数的和
private int sum(int n)
{
int sum = 0;
while(n>0)
{
sum += bit
;
n -= dealBit(n);
}
return sum;
}
     (2)树状数组的更新
      将第i项的值加上val,并且将i中最小位的1加到i上,直到i大于树状数组的长度。
      例如,将树状数组下标为3的值加1,此时树状数组下标为3的值为4,之后将3(0011)中最小位的1加到3上得到4(0100),再将下标为4的值加1,此时下标为4的值为11.示例代码如下://当nums某一位加上一个数时,用于改变整个树状数组.
private void add(int index,int num)
{
while(index<=bit.length-1)
{
bit[index] += num;
index += dealBit(index);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: