1. 程式人生 > >二叉索引樹(樹狀數組)入門(一)

二叉索引樹(樹狀數組)入門(一)

太差 數據結構 pri ret 進行 中一 class 這就是 說過

二叉索引樹,即樹狀數組,被某神犇稱之為是最漂亮的數據結構,所以蒟蒻北籬也去學習了一下傳說中的樹狀數組。

限於蒟蒻北籬的語言表達能力太差(其實是懶),於是引用了度娘的一段對樹狀數組的解釋

樹狀數組(Binary Indexed Tree(B.I.T), Fenwick Tree)是一個查詢和修改復雜度都為log(n)的數據結構。主要用於查詢任意兩位之間的所有元素之和,但是每次只能修改一個元素的值;經過簡單修改可以在log(n)的復雜度下進行範圍修改,但是這時只能查詢其中一個元素的值(如果加入多個輔助數組則可以實現區間修改與區間查詢)。

樹狀數組可以O(logn)的完成單點修改,單點查詢和區間查詢等操作,其實就是動態維護前綴和的過程,其中的所有操作都是以lowbit(x)

操作為核心的。

lowbit

先來說一下lowbit(x)操作,lowbit(x)操作其實就是求x的二進制最低位(最低位代表的數字而不是位數),求x的lowbit也很簡單,代碼如下:

1 inline int lowbit(int x) {
2     return x&(-x);
3 }

其實就是利用了補碼,證明很簡單,這裏略過。如果你不想寫函數也可以宏定義:

1 #define lowbit(x) ((x)&(-(x)))

加多層括號是為了防止優先級錯誤,不必要時完全可以不加。

存儲結構

樹狀數組看名字就知道只是一個數組,沒什麽好說的,但是,每個數組元素存儲的不是原數組中的內容(這不是廢話嗎),而是原數組中(x-lowbit(x),x]內所有元素的和,而這就是我們查詢時間為O(logn)的原因。

單點修改

如果要把數組中下標為x的元素加上t,那麽學過數組的人肯定都會寫:

1 inline void change(int x,int t) {
2     c[x]+=t;
3 }

這樣就行了嗎?不行。

我們在說存儲結構的時候就說過了,樹狀數組中下標為元素的值等於原數組中(x-lowbit(x),x]內所有元素的和,所以我們修改樹狀數組的其他元素,我們假設樹狀數組有n個元素,於是代碼如下:

1 inline void change(int x,int t) {
2     for(;x<=n;x+=lowbit(x)) {
3         c[x]+=t;
4     }
5 }

這樣才是沒有問題的修改操作。如果看懂了上面的存儲原理想要理解的話也不難,理解不了就多看幾遍圖就懂了。

有沒有發現我們並沒有說建樹的代碼?那是因為直接把每一個點調用一次change操作就好了。

前綴和查詢

我們利用樹狀數組可以做到O(logn)的前綴和查詢,和單點修改類似的原理,這裏不再多說,看代碼就能懂的(笑

1 inline int sum(int x) {
2     int s=0;
3     for(;x;x-=lowbit(x)) {
4         s+=c[x];
5     }
6     return s;
7 }

區間查詢

我們可以利用樹狀數組求[s,t]內所有元素的和,做法很簡單,既然我們已經寫出前綴和查詢的代碼,我們現在就要好好利用它,求出sum(t)和sum(s),然後兩者相減就能得出答案,所以區間求和的時間也是O(logn)。

1 inline int query(int s,int t) {
2     return sum(t)-sum(s-1);
3 }

樹狀數組模板

最後丟一個模板好了(溜

 1 struct BIT{
 2     private:
 3         static const int maxn=5e5+10;
 4         int c[maxn],n;
 5     public:
 6         void init(int n) {
 7             memset(c,0,sizeof(c));
 8             this->n=n;
 9         }
10         inline void change(int x,int t) {
11             for(;x<=n;x+=lowbit(x)) {
12                 c[x]+=t;
13             }
14         }
15         inline int sum(int x) {
16             int s=0;
17             for(;x;x-=lowbit(x)) {
18                 s+=c[x];
19             }
20             return s;
21         }
22         inline int query(int s,int t) {
23             return sum(t)-sum(s-1);
24         }
25 };

二叉索引樹(樹狀數組)入門(一)