樹狀數組(Binary Indexed Tree(BIT), Fenwick Tree)是一個查詢和修改複雜度都爲log(n)的數據結構。主要用於查詢任意兩位之間的全部元素之和,可是每次只能修改一個元素的值;通過簡單修改能夠在log(n)的複雜度下進行範圍修改,可是這時只能查詢其中一個元素的值(若是加入多個輔助數組則能夠實現區間修改與區間查詢)。算法
百度上給出了使人難以理解的概念,其實這個東西我也是琢磨了一天,參考了大量博客的筆記才搞清楚了大體思路和原理,說說心得吧!數組
先看兩幅圖(網上找的,若是雷同,不要大驚小怪~),下面的說明都是基於這兩幅圖的,左邊的叫A圖吧,右邊的叫B圖:數據結構
是否是很像一顆樹?對,這就是爲何叫樹狀數組了~先看A圖,a數組就是咱們要維護和查詢的數組,可是其實咱們整個過程當中根本用不到a數組,你能夠把它看成一個擺設!c數組纔是咱們全程關心和操縱的重心。先由圖來看看c數組的規則,其中c8 = c4+c6+c7+a8,c6 = c5+a6……先沒必要糾結怎麼作到的,咱們只要知道c數組的大體規則便可,很容易知道c8表示a1~a8的和,可是c6倒是表示a5~a6的和,爲何會產生這樣的區別的呢?或者說發明她的人爲何這樣區別對待呢?答案是,這樣會使操做更簡單!看到這相信有些人就有些感受了,爲何複雜度被lg了呢?能夠看到,c8能夠看做a1~a8的左半邊和+右半邊和,而其中左半邊和是肯定的c4,右半邊其實也是一樣的規則把a5~a8一分爲二……繼續下去都是一分爲二直到不能分,能夠看看B圖。怎麼樣?是否是有點二分的味道了?對,說白了樹狀數組就是巧妙的利用了二分,她並不神祕,關鍵是她的巧妙!函數
她又是怎樣作到不斷的一分爲二呢?說這個以前我先說個叫lowbit的東西,lowbit(k)就是把k的二進制的高位1所有清空,只留下最低位的1,好比10的二進制是1010,則lowbit(k)=lowbit(1010)=0010(2進制),介於這個lowbit在下面會常常用到,這裏給一個很是方便的實現方式,比較廣泛的方法lowbit(k)=k&-k,這是位運算,咱們知道一個數加一個負號是把這個數的二進制取反+1,如-10的二進制就是-1010=0101+1=0110,而後用1010&0110,答案就是0010了!明白了求解lowbit的方法就能夠了,繼續下面。介於下面討論十進制已經沒有意義(這個世界原本就是二進制的,人非要主觀的構建一個十進制),下面全部的數沒有特別說明都看成二進制。this
上面那麼多文字說lowbit,還沒說它的用處呢,它就是爲了聯繫a數組和c數組的!ck表示從ak開始往左連續求lowbit(k)個數的和,好比c[0110]=a[0110]+a[0101],就是從110開始計算了0010個數的和,由於lowbit(0110)=0010,能夠看到其實只有低位的1起做用,由於很顯然能夠寫出c[0010]=a[0010]+a[0001],這就爲何咱們任何數都只關心它的lowbit,由於高位不起做用(基於咱們的二分規則它必須如此!),除非除了高位其他位都是0,這時自己就是lowbit。spa
既然關係創建好了,看看如何實現a某一個位置數據跟改的,她不會直接改的(開始就說了,a根本不存在),她每次改其實都要維護c數組應有的性質,由於後面求和要用到。而維護也很簡單,好比更改了a[0011],咱們接着要修改c[0011],c[0100],c[1000],這是很容易從圖上看出來的,可是你可能會問,他們之間有申明必然聯繫嗎?每次求解總不能總要拿圖來看吧?其實從0011——>0100——>1000的變化都是進行「去尾」操做,又是本身造的詞--'',我來解釋下,就是把尾部應該去掉的1都去掉轉而換到更高位的1,記住每次變換都要有一個高位的1產生,因此0100是不能變換到0101的,由於沒有新的高位1產生,這個變換過程剛好是能夠藉助咱們的lowbit進行的,k +=lowbit(k)。code
好吧,如今更新的次序都有了,可能又會產生新的疑問了:爲何它非要是這種關係啊?這就要追究到以前咱們說c8能夠看做a1~a8的左半邊和+右半邊和……的內容了,爲何c[0011]會影響到c[0100]而不會影響到c[0101],這就是以前說的c[0100]的求解其實是這樣分段的區間 c[0001]~c[0001] 和區間c[0011]~c[0011]的和,數字過小,可能這樣不太理解,在好比c[0100]會影響c[1000],爲何呢?由於c[1000]能夠看做0001~0100的和加上0101~1000的和,可是0101位置的數變化並會直接做用於c[1000],由於它的尾部1不能一下在跳兩級在產生兩次高位1,是經過c[0110]間接影響的,可是,c[0100]卻能夠跳一級產生一次高位1。orm
可能上面說的你比較繞了,那麼此時你只需注意:c的構成性質(實際上是分組性質)決定了c[0011]只會直接影響c[0100],而c[0100]只會直接影響[1000],而下表之間的關係剛好是也必須是k +=lowbit(k)。此時咱們就是寫出跟新維護樹的代碼:htm
1 void add(int k,int num) 2 { 3 while(k<=n) 4 { 5 tree[k]+=num; 6 k+=k&-k; 7 } 8 }
1 int read(int k)//1~k的區間和 2 { 3 int sum=0; 4 while(k) 5 { 6 sum+=tree[k]; 7 k-=k&-k; 8 } 9 return sum; 10 }
下面給出一道模版題吧!blog
POJ 2352
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 45080 | Accepted: 19567 |
Description
Input
Output
Sample Input
5 1 1 5 1 7 1 3 3 5 5
Sample Output
1 2 1 1 0
Hint
Source
就是求每一個小星星左小角的星星的個數。座標按照Y升序,Y相同X升序的順序給出
因爲y軸已經排好序,能夠按照x座標創建一維樹狀數組
1 #include <stdio.h> 2 #include <string.h> 3 const int MAXN=32005; 4 const int MINN=15005; 5 int tree[MAXN];//下標爲橫座標 6 int level[MINN];//下標爲等級數 7 /*int lowerbit(int x) 8 { 9 return x&-x; 10 }*/ 11 void add(int k,int num) 12 { 13 while(k<=MAXN) 14 { 15 tree[k]+=num; 16 k+=k&-k; 17 } 18 } 19 int read(int k)//1~k的區間和 20 { 21 int sum=0; 22 while(k) 23 { 24 sum+=tree[k]; 25 k-=k&-k; 26 } 27 return sum; 28 } 29 int main() 30 { 31 int n,x,y,i; 32 memset(tree,0,sizeof(tree)); 33 memset(level,0,sizeof(level)); 34 while(scanf("%d",&n)!=EOF) 35 { 36 for(i=1;i<=n;i++) 37 { 38 scanf("%d%d",&x,&y); 39 int temp=read(x+1);//加入x+1,是爲了不0,X是可能爲0的 40 level[temp]++; 41 add(x+1,1); 42 } 43 for(i=0;i<n;i++) 44 printf("%d\n",level[i]); 45 } 46 return 0; 47 }