您的位置:首页 > 其它

树状数组(Binary Indexed Tree)

2011-08-09 00:37 495 查看
树状数组(BIT - Binary Indexed Tree)是一个用数组表示的树型数据结构,最早用于频次累计表中。树状数组中每个元素维护一个频次表A的特定部分区段的累加和。假设输入动态序列为A,A中元素值可以被修改,如果使用直接蛮力法来查询一个区间i..j的累加和,复杂度为O(n),而树状数组由于每个元素存储的是部分累加和,通过数组下标的二进制索引规律可以在O(logn)时间内找到1...j或i...j的累加和。存储空间只需要维护树状数组T,A的元素值可以很容易地从T得到(即特例i=j时的区间查询)。

数组A下标通常从0开始,而树状数组的有效下标是从1开始。下面关于lowbit的讨论假设数组A下标从1开始。

树状数组中元素在树型结构中的位置是根据数组下标的lowbit函数值2^r来确定的。定义每一个元素T[i]的值等于A[i-2^r + 1] + ... + A[i],即T[i]表示共2^r个元素的部分累加和,或者说T[i]元素管辖区段从i开始往前推2^r个元素。2^r的计算方法很简单,就是i & (-i),原理是利用负数补码等于相应正数值取反加一,例如当i=6时,对应的二进制数是0110,取反得到1001,加一得到1010,从而2^r=lowbit(i)=0110 & 1010 = 10,即2^r等于十进制值2,或者说T[6]管辖区段长度为2,
即T[6] = A[5] + A[6]。

有了lowbit值,可以高效地查询数列A从1到i的区间所有元素的累加和:即将若干个T[j]累计即可(j>=1),设T[j]管辖的区段长度为lowbit(j),那么T[j-1]管辖的区段的最高下标值可以通过j = j - lowbit(j)得到;也可以低代价更新数列A的某个元素,这时需要更新受影响的T中相应元素,在树型结构中,某一个结点的父结点就是受影响的结点之一,而T[j]的父结点管辖的区段的最高下标值可以通过j = j + lowbit(j)得到 (j<=n)。查询区间可以用差分方法: 如果i<=j,那么sum(A[i..j])
= query(j) - query(i-1)。

实现:

/**
*
* Binary Indexed Tree (BIT)
* Peter M. Fenwick: A New Data Structure for Cumulative Frequency Tables
*
* Copyright (c) 2011 ljs (http://blog.csdn.net/ljsspace/)
* Licensed under GPL (http://www.opensource.org/licenses/gpl-license.php)
*
* @author ljs
* 2011-08-09
*
*/
public class BinaryIndexTree {
private int[] T;

public BinaryIndexTree(int n) {
//each element in T is initialized to 0
T = new int[n + 1];
}

private int lowbit(int idx){
return idx & (-idx);
}

//idx starts with 1, not 0
public void update(int idx, int val) {
while (idx < T.length) {
T[idx] += val;
idx += lowbit(idx);
}
}
//idx starts with 1, not 0
public int query(int idx) {
int sum = 0;
while (idx > 0) {
sum += T[idx];
idx -= lowbit(idx);
}
return sum;
}
//get the sum from A[i] to A[j] inclusively
public int queryInterval(int i,int j){
if(j<i){
//swap
int tmp=j;j=i;i=tmp;
}
return query(j)-query(i-1);
}

private void report(){
int n = T.length - 1;
//report
System.out.print("A from tree=");
for(int i=1;i<=n;i++){
int ai = query(i)-query(i-1);
System.out.format(" %d",ai);
}
System.out.println();

//tree
System.out.format("T[1..%d]=",n);
for(int j=1;j<=n;j++){
System.out.format(" %d",T[j]);
}
System.out.println();

//query
System.out.print("sums = ");
for(int j=1;j<=n;j++){
int r = this.query(j);
System.out.format(" %d",r);
}
System.out.println();

}
public static void main(String[] args) throws Exception {
int[] A = new int[]{2,3,4,5,6};
int n = A.length;
BinaryIndexTree bit = new BinaryIndexTree(n);

//update all elements
for(int i=0,j=1;i<n;i++,j++){
bit.update(j, A[i]);
}
//update a single element
int val = 4;
int idx = 3;
A[idx-1] += val;
bit.update(idx, val);

System.out.format("A[0..%d] =  ",n-1);
for(int i=0;i<n;i++){
System.out.format(" %d",A[i]);
}
System.out.println();

bit.report();

//sum query
int q=3;
int r = bit.query(q);
System.out.format("sum A[%d..%d]: %d%n", 0,q-1,r);

//interval query
int p = 2;
q=4;
r = bit.queryInterval(p, q);
System.out.format("sum A[%d..%d]: %d%n", p-1,q-1,r);

System.out.println("*****************");
A = new int[]{1,0,2,1,1,3,0,4,2,5,2,2,3,1,0,2};
n = A.length;
bit = new BinaryIndexTree(n);

//update all elements
for(int i=0,j=1;i<n;i++,j++){
bit.update(j, A[i]);
}
System.out.format("A[0..%d] =  ",n-1);
for(int i=0;i<n;i++){
System.out.format(" %d",A[i]);
}
System.out.println();

bit.report();
}
}


输出:

A[0..4] = 2 3 8 5 6

A from tree= 2 3 8 5 6

T[1..5]= 2 5 8 18 6

sums = 2 5 13 18 24

sum A[0..2]: 13

sum A[1..3]: 16

*****************

A[0..15] = 1 0 2 1 1 3 0 4 2 5 2 2 3 1 0 2

A from tree= 1 0 2 1 1 3 0 4 2 5 2 2 3 1 0 2

T[1..16]= 1 1 2 4 1 4 0 12 2 7 2 11 3 4 0 29

sums = 1 1 3 4 5 8 8 12 14 19 21 23 26 27 27 29

参考资料:

Peter M. Fenwick (1994): A New Data Structure for Cumulative Frequency Tables

TopCoder - Binary Indexed Trees

NOCOW-树状数组
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: