树状数组
是什么
树状数组,又叫二叉索引树(BIT,Binary Indexed Tree),以其发明者命名为Fenwick树。其初衷是解决数据压缩里累计频率的计算问题,现多用于高效计算数列的前缀和,区间和。
累计频率
索引+分治
要求前缀和:只需要考虑前找规律即可。
取最后一个1:
单点更新
向右边找:
总结
-
树状数组用于求解给定数组中任意区间的累计频率(前缀和,区间和),支持单点更新,不支持增加和删除元素。
-
树状数组的本质也是一个数组,根据元素额索引建立里“逻辑上的”树形结构,元素之间便有了练习,所以也叫二叉索引树。
-
一般情况:
- 初始化:遍历所有元素:O(n) 更新到BIT中O(logn) 总计O(nlogn);
- 查询O(logn);
- 单点更新O(logn);
线段树
为什么线性数组求前缀和效率差
递归(后序遍历)实现查询和更新
建树
//递归建树:后序遍历
static void buildTree(int[] arr,int[] tree,int node,int start,int end){
if(start==end){
tree[node]=arr[start];
return;
}
int left_node=2*node+1;
int right_node=2*node+2;
int mid=(start+end)/2;
buildTree(arr, tree, left_node, start, mid);
buildTree(arr, tree, right_node, mid+1, end);
tree[node]=tree[left_node]+tree[right_node];
}
更新
//递归查找:后序遍历 执行更新
static void update(int[] arr,int[] tree,int node,int start,int end,int idx,int val){
if(start==end){
arr[idx]=val;
tree[node]=val;
return;
}
int left_node=2*node+1;
int right_node=2*node+2;
int mid=(start+end)/2;
if(idx>=start&&idx<=mid){
update(arr, tree, left_node, start, mid, idx, val);
}
else
update(arr, tree, right_node, mid+1, end, idx, val);
tree[node]=tree[left_node]+tree[right_node];
}
求区间和
static int query(int[] arr,int[] tree,int node,int start,int end,int L,int R){
System.out.println("start="+start);
System.out.println("start="+end);
System.out.println();
if(L>end||R<start){
return 0;
}
if(L<=start&&end<=R){
return tree[node];
}
if(start==end){
return tree[node];
}
int left_node=2*node+1;
int right_node=2*node+2;
int mid=(start+end)/2;
int sum_left=query(arr, tree, left_node, start, mid, L, R);
int sum_right=query(arr, tree, right_node, mid+1, end, L, R);
return sum_left+sum_right;
}
测试
public static void main(String[] args) {
int[] arr={1,3,5,7,9,11};
int size=6;
int[] tree=new int[MAX_LEN];
//0号节点,表示区间和[0,5]
buildTree(arr, tree, 0, 0, size-1);
// for (int i = 0; i < 15; i++) {
// System.out.println("tree["+i+"]="+tree[i]);
// }
System.out.println();
update(arr, tree, 0, 0, size-1,4,6);
for (int i = 0; i < 15; i++) {
System.out.println("tree["+i+"]="+tree[i]);
}
System.out.println();
System.out.println(query(arr, tree, 0, 0, size-1,2,4));
}
总结
树状数组:数学
线段树:树递归
树状数组,线段树大法好!!!!!!