树状数组Binary Indexed Trees详解与Java实现
2016-07-09 22:16
405 查看
WiKi
树状数组是由Peter Fenwick在1994年提出的,所以又称为Fenwick Tree。数组的区间求和的复杂度是O(n),树状数组可以将数组区间求和的复杂度降低到O(lg n)。这对于长数组的高频率区间求和的应用场景来讲,可以提高效率。参考
树状数组(Binary Indexed Trees)搞懂树状数组
详解
这里从上面的参考中总结我的思路。树状数组通过树形结构对原始数组进行预处理,树的每个节点存储了原始数组的某些区间和,这样,原始数组的区间和就可以通过树形结构将算法复杂度降低到O(lg n)。
例如,下图a数组是原始数组,而c数组就是对a数组进行预处理后的树行结构。
从a数组如何获得c数组呢?这里要借助lowBit(int K)函数。lowBit(int K)函数保留k的二进制最低位1的值。例如,1110保留最低位1即0010。
private static int lowBit(int k){ return k&-k; }
从a数组和c数组满足下面的累加公式。即,c[i]等于从a数组从i开始向左累加lowBit(i)个值。
例如:
c[1]等于a数组从1开始向左累加lowBit(1)=1个,即c[1]=a[1];
c[2]等于a数组从2开始向左累加lowBit(2)=2个,即c[2]=a[2]+a[1];
……
c[7]等于a数组从2开始向左累加lowBit(7)=1个,即c[2]=a[7];
c[8]等于a数组从2开始向左累加lowBit(8)=8个,即c[2]=a[8]+a[7]+…a[1];
那么,构造好了c数组,求和就变得简单了。
直到求和下标为1即累加到了a[1]。
代码实现:
/** * 计算1~index范围内和 * index一直减去lowBit(index),直到index为0 * */ public int sum(int index){ if (index<1&&index>length) { throw new IllegalArgumentException("Out of Range!"); } int sum=0; while (index>0) { sum+=tree[index]; index-=lowBit(index); } return sum; }
到这里,求和的方式已经介绍完毕。
两点注意:
1、实际上并没有一个原始数组a一直存在,否则就多耗费了一倍的存储空间。
2、数组一般从1开始算有效位,0位置为无效位,这样不容易混淆。
Java实现
这里实现了BinaryIndexedTree的类。/**
* 树状数组的Java版本,created by 曹艳丰 2016.07.09
* 原理参考:http://www.hawstein.com/posts/binary-indexed-trees.html
* 或:https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/
* */
public class BinaryIndexedTree {
public int length;
private int[] tree;
/**
* 为了统一下标,所以tree[0]不被使用,数组有效范围1~length。
* */
public BinaryIndexedTree(int length){
this.length=length;
tree=new int[length+1];
}
/** * 计算1~index范围内和 * index一直减去lowBit(index),直到index为0 * */ public int sum(int index){ if (index<1&&index>length) { throw new IllegalArgumentException("Out of Range!"); } int sum=0; while (index>0) { sum+=tree[index]; index-=lowBit(index); } return sum; }/**
* 计算start~end范围内和
* */
public int sum(int start,int end) {
return sum(end)-sum(start-1);
}
/**
* index一直加上lowBit(index),直到index为length。这些位置的值都加上value
* */
public void put(int index,int value){
if (index<1&&index>length) {
throw new IllegalArgumentException("Out of Range!");
}
while (index<=length) {
tree[index]+=value;
index+=lowBit(index);
}
}
/**
* index一直减去lowBit(index),直到index为length。这些位置的值都加上value
* */
public int get(int index){
if (index<1&&index>length) {
throw new IllegalArgumentException("Out of Range!");
}
int sum=tree[index];
int z=index-lowBit(index);
index--;
while (index!=z) {
sum-=tree[index];
index-=lowBit(index);
}
return sum;
}
/**
* 保留k的二进制最低位1的值。例如,1110保留最低位1即0010.
* */
private static int lowBit(int k){ return k&-k; }}
并进行了测试
import java.util.Random; public class Main { public static void main(String[] args) { // TODO Auto-generated method stub int length=15; BinaryIndexedTree bTree=new BinaryIndexedTree(length); Random random=new Random(); //随机放满数据 for (int i = 1; i <= bTree.length; i++) { bTree.put(i, random.nextInt(100)); } //取出每一位 for (int i = 1; i <= bTree.length; i++) { int value=bTree.get(i); System.out.printf("%3d",value); System.out.print(" "); } System.out.println(); //计算0~i的和 for (int i = 1; i <= bTree.length; i++) { int sum=bTree.sum(i); System.out.printf("%3d",sum); System.out.print(" "); } System.out.println(); //计算start~end的和 System.out.printf("%3d",bTree.sum(2,4)); } }
相关文章推荐
- Codeforces Coder-Strike 2014 - Finals (online edition, Div. 1)
- HDU 1166 敌兵布阵
- POJ 2352 Stars
- PAT 1057 Stack (30)
- neu 1438 树状数组求逆序数
- poj 2352
- 树状数组
- 树状数组 Binary Indexed Tree
- hdu 4630 No Pain No Game 树状数组
- 树状数组区间求和的三种模型
- HDOJ1166 敌兵布阵 树状数组
- 二维树状数组
- Hoj 2275 Number Sequence
- POJ3321 Apple Tree
- BZOJ2434 [Noi2011]阿狸的打字机【AC自动机+dfs序+树状数组】
- [离散化+树状数组]逆序对
- HDU 3015
- hdu 5147
- 求逆序数
- 树状数组