樹狀數組(Binary Indexed Tree(B.I.T), Fenwick Tree)是一個查詢和修改複雜度都爲log(n)的數據結構,空間複雜度則爲O(n),通過將線性結構轉化成樹狀結構,從而進行跳躍式掃描。主要用於查詢任意兩位之間的所有元素之和,但是每次只能修改一個元素的值;經過簡單修改可以在log(n)的複雜度下進行範圍修改,但是這時只能查詢其中一個元素的值(如果加入多個輔助數組則可以實現區間修改與區間查詢)。
A數組是原數組,c數組是樹狀數組,可以發現:
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
對任意n ,C[n]=A[n-2^k+1]+……A[n],k爲n在二進制下末尾0的個數,例如n=6(110),k=1.
- Lowbit:
用函數Lowbit(x)可以求出2^k:
int Lowbit(int x)
{
return x&(-x); //或return x&(x^(x-1));
}
這裏x&(-x)利用了機器補碼的特性。
- 區間查詢:
sum(a,b)=sum(1,b)-sum(1,a-1)任意求和均可轉換爲求sum(1,n)
n=7:
sum(1,7)=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7] ;
C[4]=A[1]+A[2]+A[3]+A[4]; C[6]=A[5]+A[6]; C[7]=A[7];
可以推出: sum[7]=C[7]+C[6]+C[4];
序號寫爲二進制: sum(1,(111))=C[(111)]+C[(110)]+C[(100)];
int sum(int x)
{
int sum = 0;
while (x > 0)
{
sum += c[x];
x -= Lowbit(x);
}
return sum;
}
x-=Lowbit(x)實際上就是將x二進制最後一個1減去,得到上一個父節點。而n的二進制裏最多有log(n)個1,所以查詢效率是log(n)的。
- 單點更新:
要對最底層的值a[x]進行更新時,所有與它相關的父節點存儲的和也需要進行更新。最壞情況下爲修改第一個元素,最多有log(n)個祖先。
void update(int pos, int num)
{
while (pos <= N) //元素個數N
{
c[pos] += num;
pos += Lowbit(x);
}
}
侷限性:
不能進行區間更新;只能求和,不能求最值。
能用樹狀數組的都可以用線段樹。