樹狀陣列(binary indexed tree)
阿新 • • 發佈:2019-02-14
樹狀陣列是一種用於查詢區間和的資料結構。
比如對於陣列a[1,2,3,4,5,6,7,8,9,10],我們要知道它的前4項和,我們就需要把這前四個一個個加起來才能得到結果,也就是O(K)(前k項和)的複雜度。如果查詢次數少,k也比較小還可以接受,但是在大規模的查詢和資料量時,這個複雜度就太高了。樹狀陣列就是為了解決這個問題而創造出來的,它能在log(k)的複雜度內查詢區間和。
樹狀陣列其實就是把一顆二叉樹壓縮在了一個數組裡,看下圖(一如既往的醜):
對於陣列[1,2,3,4],我們把它轉化為一個求和樹,每個節點的值就是它的兩個子節點的值,此時我們要求前k項就比較好求了,比如求前4項,我們可以直接給出節點10。但是我們如何用一個樹組來表示它呢?如果要每個節點都有一個位置對應,起碼得有2n個節點,但是我們只用n個。考慮b,d兩個節點,其實b裡面已經包含了d,所以我們可以把他們合併。同樣,a裡面其實包含了e,g他們也可以合併到位置4。
我們來看看一般的規律(下面的位置索引都是二進位制值):
索引 | 包含值 |
---|---|
001(1) | 001 |
010(2) | 001 010 |
011(3) | 011 |
100(4) | 001 010 011 100 |
要得到從左到右第一個1也很簡單 x&(-x)即可。
下面是具體程式碼:
#include<iostream> using namespace std; //Binary Indexed Tree(BIT) //得到x只剩下從右到左第一個一時的值。 int getk(int x){ return x&(-x); } //由正常的陣列建立樹狀陣列 void build(int s[],int size){ for(int i=1;i<=size;i++){ int next = getk(i)+i; if(next<=size)s[next-1] += s[i-1]; } } //更新一個位置的值 void update(int s[],int size,int k,int dt){ k++; while(k<=size){s[k-1]+=dt;k+=getk(k);} } //求前k項和 int ksum(int s[],int size,int k){ int result = 0; while(k>1){cout << k << endl;result += s[k-1];k -= getk(k);} return result; } int main(){ int a[10] = {1,2,3,4,5,6,7,8,9,10}; build(a,10); cout << ksum(a,10,4) << " " << ksum(a,10,7) << endl; update(a,10,4,-1); for(int i=0;i<10;i++){ cout << a[i] << " "; } cout << endl; }