数据结构之树状数组
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},现将其以两个一组作为子节点,求得二者之和作为其父节点,重复该操作,直到最终只剩下一个父节点,可以得到以下该树:
![](https://img-blog.csdn.net/20180226180905254)
那么现在看着所构造出来的这棵树来想一个问题,怎样求数组中下标从i到j的和?其实我们可以换一个角度来思考求i到j的和可以转化为((1到j的和)-(1到i-1的和))。仔细观察上面的树,不难发现在求解1到x的和过程中灰色部分的节点均不需要使用,因为不论在求和或修改树状数组两种操作中要使用这些右节点,那么其兄弟节点一定会被用到,这样的话直接使用其父节点即可(例如,求1到3的和。只需要将自上而下第二层的值为3的节点与第三层值为3的节点相加即可)。那么我们将这些冗余的节点删去,并且加以编号就可以得到一个树状数组,如下图所示:
![](https://img-blog.csdn.net/20180226191024480)
给各节点编号也有一种特殊的规律:节点编号二进制数为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);
}
}
省去过多的长篇大论的理论介绍,通过一个简单的例子来对树状数组进行一个初步的了解和认识。假设有一个数组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);
}
}
相关文章推荐
- 【数据结构之树状数组】
- Codeforces Round #248 (Div. 2) B题 【数据结构:树状数组】
- 【数据结构之树状数组】
- 【数据结构之树状数组】从零认识树状数组
- 【NOIp复习】数据结构之树状数组
- 数据结构之树状数组
- BZOJ3790 神奇项链 解题报告【字符串】【Manacher】【树状数组】【数据结构优化DP】
- 数据结构之树状数组
- Uva 11990 "Dynamic" Inversion(树状数组 + 数据结构分块)
- Codeforces Round #248 (Div. 2) B称号 【数据结构:树状数组】
- 数据结构之树状数组
- 数据结构之树状数组
- 数据结构之树状数组
- HDU 4417 超级马里奥 数据结构+利用树状数组进行快速统计+多维统计转换
- 树状数组学习 ----- 数据结构
- CodeVS1369 xth 砍树 解题报告【数据结构】【线段树/树状数组】
- 树状数组---数据结构
- 数据结构之树状数组
- 数据结构(树状数组)HDU1166
- 数据结构之树状数组