树状数组知识入门
2017-07-16 20:28
232 查看
树状数组(Binary Indexed Tree(BIT), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改。
如百度上所讲 :在解题过程中,我们有时需要维护一个数组的前缀和 S[i]=A[1]+A[2]+…+A[i]。但是不难发现,如果我们修改了任意一个 A[i],S[i]、 S[i+1]…S
都会发生变化。可以说,每次修改 A[i]后,调整前缀和 S 在最坏情况下会需要 O(n)的时间。当 n 非常大时,程序会运行得非常缓慢。因此,这里我们引入“树状数组”,它的修改与求和都是 O(logn)的,效率非常高。
具体如下图
令这棵树的结点编号为C1,C2…Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
…
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
这个规律是怎么实现的呢?
这里有一个有趣的性质:
设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
所以很明显:Cn = A(n – 2^k + 1) + … + An
用最小幂 2^k建立树状数组c、并且能进行更新元素值、子序列求和等。对于这个最小幂 2^k(k为x二进制末尾0的个数)
算这个2^k有一个快捷的办法,定义一个函数如下即可:
int lowerbit(int x)
{
return x&(x^(x–1));
}
利用机器补码特性,也可以写成:
int lowerbit(int x)
{
return x&-x;
}
对树的维护代码,也就是对原有数据进行修改
有了上面的基础,求和就比较简单了。比如求0001~0110的和就直接c[0100]+c[0110],分析方法与上面的恰好逆过来,而且写法也是逆过来的:
数组和的初始计算,通过lowbit计算出树状数值。
如百度上所讲 :在解题过程中,我们有时需要维护一个数组的前缀和 S[i]=A[1]+A[2]+…+A[i]。但是不难发现,如果我们修改了任意一个 A[i],S[i]、 S[i+1]…S
都会发生变化。可以说,每次修改 A[i]后,调整前缀和 S 在最坏情况下会需要 O(n)的时间。当 n 非常大时,程序会运行得非常缓慢。因此,这里我们引入“树状数组”,它的修改与求和都是 O(logn)的,效率非常高。
具体如下图
令这棵树的结点编号为C1,C2…Cn。令每个结点的值为这棵树的值的总和,那么容易发现:
C1 = A1
C2 = A1 + A2
C3 = A3
C4 = A1 + A2 + A3 + A4
C5 = A5
C6 = A5 + A6
C7 = A7
C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
…
C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16
这个规律是怎么实现的呢?
这里有一个有趣的性质:
设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax,
所以很明显:Cn = A(n – 2^k + 1) + … + An
用最小幂 2^k建立树状数组c、并且能进行更新元素值、子序列求和等。对于这个最小幂 2^k(k为x二进制末尾0的个数)
算这个2^k有一个快捷的办法,定义一个函数如下即可:
int lowerbit(int x)
{
return x&(x^(x–1));
}
利用机器补码特性,也可以写成:
int lowerbit(int x)
{
return x&-x;
}
对树的维护代码,也就是对原有数据进行修改
void add(int k,int num) //从k改变 k后存储的数组的和也要改变 { while(k<=n) { tree[k]+=num; k+=lowbit(k); } }
有了上面的基础,求和就比较简单了。比如求0001~0110的和就直接c[0100]+c[0110],分析方法与上面的恰好逆过来,而且写法也是逆过来的:
int read(int k)//1~k的区间和 { int sum=0; while(k) { sum+=tree[k]; k-=k&-k; } return sum; }
数组和的初始计算,通过lowbit计算出树状数值。
void initsum() { for(int i=n;i>=1;i--) { int num=0; for(int j=i-lowbit(i)+1;j<=i;j++) num+=t[j]; t[i]=num; } }
相关文章推荐
- PKU 2299 求解逆序数(使用归并或者树状数组) 树状数组及入门知识
- poj 2299 树状数组入门
- 树状数组入门—逆序对统计
- Solidity基础知识入门(五)固定大小字节数组
- 树状数组基础知识
- POJ-1990 MooFest (树状数组 入门题)
- HDU 1166 敌兵布阵 (树状数组入门)
- poj 2155 二维线段树或树状数组入门
- js入门知识(非行间样式、数组)
- hdu 1166 线段树与树状数组入门 单点更新
- Ural1028(树状数组入门)
- nyoj 522 树状数组入门
- poj 3067 Japan 二维树状数组(入门)
- 树状数组与线段树入门
- HDU 2689 Sort it 简单树状数组入门
- 树状数组入门
- Solidity基础入门知识(四)枚举、存储类型和数组
- hdu 1166 树状数组 入门题
- 前端入门知识.....有关数组的一些方法.....
- 树状数组彻底入门