樹狀數組
樹狀數組(Binary Indexed Tree(BIT), Fenwick Tree)是一個查詢和改動複雜度都爲log(n)的數據結構。主要用於查詢隨意兩位之間的所有元素之和,但是每次僅僅能改動一個元素的值;通過簡單改動可以在log(n)的複雜度下進行範圍改動,但是這時僅僅能查詢當中一個元素的值。
基本概念:
若是數組a[1..n],那麼查詢a[1]+...+a[n]的時間是log級別的,而且是一個在線的數據結構,支持隨時改動某個元素的值,複雜度也爲log級別。
來觀察這個圖:
樹狀數組的結構圖數組
令這棵樹的結點編號爲C1,C2...Cn。令每個結點的值爲這棵樹的值的總和,那麼easy發現:
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
這裏有一個有趣的性質:
咱們來看一下這個規律,
C1(0001)末尾二進制0的個數是0,並且C1管轄的範圍是C1=A1;
C2(0010)末尾二進制0的個數是1,並且C2管轄的範圍是C2=A1+A2;
C3(0011)末尾二進制0的個數是0,並且C3管轄的範圍是C3=A3;
......
C8(1000)末尾二進制0的個數是3,並且C2管轄的範圍是C8=A1+A2+A3+A4+A5+A6+A7+A8;
設節點編號爲x,那麼這個節點管轄的區間爲2^k(當中k爲x二進制末尾0的個數)個元素。因爲這個區間最後一個元素一定爲Ax,
因此很是明顯:Cn = A(n – 2^k + 1) + ... + An
基本操做:
1,對於C[i]=a[i - 2^k + 1]...a[i]的定義中,比較難以逐磨的k,他的值等於i這個數的二進制表示末尾0的個數. 如4的二進制表示0100,此時k就等於2,而實際上咱們還會發現2^k就是前一位的權值,即0100中,2^2=4,恰好 是前一位數1的權值.因此因此2^k可以表示爲n&(n^(n-1))或更簡單的n&(-n),好比:
爲了表示簡便,若是現在一個int型爲4位,最高位爲符號位數據結構
int i=3&(-3); 此時i=1,3的二進制爲0011,-3的二進制爲1101(負數存的是補碼)因此0011&1101=1spa
int j=4&(-4); 此時j=4,理由同上.....code
因此計算2^k咱們可以用例如如下代碼:blog
int lowbit(int n)
{
return n&(-n);
}
2,求和操做
在上面的示意圖中,若咱們需要求sum[1..7]個元素的和,僅需要計算c[7]+c[6]+c[4]的和就能夠,到底時間複雜度怎麼算呢?一共要進行多少次求和操做呢?
求sum[1..k],咱們需查找k的二進制表示中1的個數次就能獲得終於結果,因此時間複雜度爲log(n)。ip
int GetSum(int n)
{
int sum=0;
while(n>0)
{
sum+=TreeArray[n];
n-=lowbit(n);
}
return sum;
}
n-=lowbit(n);這一項實際上等價於將當前二進制串中的最後一個1減去,因爲數的二進制串中1的個數最多有log(n)個,因此它的時間複雜度爲log(n);
以求sum[1..7]爲例,二進制爲0111,右邊第一個1出現在第0位上,也就是說要從a[7]開始向前數1個元素(僅僅有a[7]),即c[7];
而後將這個1舍掉,獲得6,二進制表示爲0110,右邊第一個1出現在第1位上,也就是說要從a[6]開始向前數2個元素(a[6],a[5]),即c[6];it
而後舍掉用過的1,獲得4,二進制表示爲0100,右邊第一個1出現在第2位上,也就是說要從a[4]開始向前數4個元素(a[4],a[3],a[2],a[1]),即c[4].io
因此s[7]=c[7]+c[6]+c[4];class
3,更新操做date
在上面的示意圖中,若是更改的元素是a[2],那麼它影響到得c數組中的元素有c[2],c[4],c[8],咱們僅僅需一層一層往上改動就可以了,這個過程的最壞的複雜度也只是O(logN);
void update(int n,int num)
{
while(n<=MAX)
{
TreeArray[n]+=num;
n+=lowbit(n);
}
}
n+=lowbit(n);這一項實際上等價於在二進制串中補0;
以改動a[2]元素爲例,需要改動c[2],2的二進制爲0010,末尾補0爲0100,即c[4]
4的二進制爲0100,在末尾補0爲1000即c[8]。因此咱們需要改動的有c[2],c[4],c[8]