【數據結構之樹狀數組】從零認識樹狀數組

1、關於樹狀數組

樹狀數組(Binary Indexed Tree,簡稱BIT),是一種修改和查詢複雜度都爲O(logN)的數據結構。但樹狀數組僅支持單點修改,在查詢時,樹狀數組也要求被查詢的區間具備可區間加減的性質。不過,樹狀數組因爲代碼實現容易、佔用空間小,經常使用於代替線段樹。html

2、詳解樹狀數組

在這裏,咱們定義原序列爲a,樹狀數組爲c,則有:算法

其中,k爲i的二進制表示中末尾0的個數,例如:i=3(101)時,k=0;i=8(1000)時,k=3。數組

定義函數lowbit(x)=2k(k爲x的二進制表示中末尾0的個數),利用機器補碼的性質,獲得:數據結構

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

根據定義,咱們能夠列出(這裏咱們用a[i,j]表示a[i]~a[j]間的全部元素的信息):ide

c[1]=a[1]函數

c[2]=a[1,2]spa

c[3]=a[3]3d

c[4]=a[1,4]code

c[5]=a[5]htm

c[6]=a[5,6]

c[7]=a[7]

c[8]=a[1,8]

c[9]=a[9]

c[10]=a[9,10]

……

首先,由樹狀數組的定義,咱們能夠獲得一個性質:

a[x]在樹狀數組中第一次出如今c[x],而且c[x]包含的區間右端點爲x

此外,經過觀察,咱們能夠發現:

a[x]僅對c[x]、c[x+lowbit(x)]、c[x+lowbit(x)+lowbit(x+lowbit(x))]……產生影響

相似於二進制拆分的思想,咱們還能夠發現:

c[1]~c[x]能夠還原出a[1]~a[x]的全部信息

因此,樹狀數組的空間複雜度就爲O(n)。

那麼,咱們就能夠寫出維護樹狀數組的代碼(這裏咱們的樹狀數組查詢的信息爲區間的和):

1 void update(int x,int val)
2 {
3      for(;x<=n;x+=lowbit(x))
4          c[x]+=val;
5      return;    
6 }

接下來,若是咱們要求區間a[i,j]的和,咱們又要怎麼作呢?

不妨這樣想,a[i,j]的和其實等於a[1,j]的和減去a[1,i-1]的和,那麼咱們只須要知道如何求一個的序列前綴和就能夠了。

首先,根據定義,咱們知道:

c[x]包含的區間長度爲lowbit(x)

在這個性質下,與維護操做相似,咱們能夠很輕鬆地寫出求前綴和的代碼:

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

以上,就是樹狀數組的基本內容了,下面咱們來看一道例題。

3、題目

Description

一行N個方格,開始每一個格子裏都有一個整數。如今動態地提出一些問題和修改:提問的形式是求某一個特定的子區間[a,b]中全部元素的和;修改的規則是指定某一個格子x,加上或者減去一個特定的值A。如今要求你能對每一個提問做出正確的回答。1≤N<100000,,提問和修改的總數m<10000條。

Input Description

輸入文件第一行爲一個整數N,接下來是n行n個整數,表示格子中原來的整數。接下一個正整數m,再接下來有m行,表示m個詢問,第一個整數表示詢問代號,詢問代號1表示增長,後面的兩個數x和A表示給位置X上的數值增長A,詢問代號2表示區間求和,後面兩個整數表示a和b,表示要求[a,b]之間的區間和。

Output Description

共m行,每一個整數

Sample Input

6

3

4

1 3 5

2 1 4

1 1 9

2 2 6

Sample Output

22

22

Data Size & Hint

1≤N≤100000, m≤10000 。

附上原題連接→_→|1080 線段樹練習|CODEVS,算法愛好者社區

雖然是線段樹練習,但在開頭咱們便講過,樹狀數組在必定狀況下能夠替代線段樹。

4、代碼實現

如下內容是一個樹狀數組單點維護並求區間和的模板,能夠經過CodeVs1080。其實核心代碼都在上文出現過,這裏只是作一個整理。

 1 #include<cstdio>
 2 const int MAXN=1e5+10;
 3 int n,m;
 4 int lowbit(int x){return x&(-x);}
 5 int BIT[MAXN];
 6 void update(int x,int val)
 7 {
 8     for(;x<=n;x+=lowbit(x))
 9         BIT[x]+=val;
10 }
11 int query(int x)
12 {
13     int ans=0;
14     for(;x;x-=lowbit(x))
15         ans+=BIT[x];
16     return ans;
17 }
18 int main()
19 {
20     scanf("%d",&n);
21     for(int i=1;i<=n;++i)
22     {
23         int a;
24         scanf("%d",&a);
25         update(i,a);
26     }
27     scanf("%d",&m);
28     for(int i=1;i<=m;++i)
29     {
30         int flag,l,r;
31         scanf("%d%d%d",&flag,&l,&r);
32         if(flag==1)update(l,r);
33         else 
34         {
35             int ans=query(r)-query(l-1);
36             printf("%d\n",ans);
37         }
38     }
39     return 0;
40 }
CodeVs1080 線段樹練習

弱弱地說一句,本蒟蒻碼字也不容易,轉載請註明出處http://www.cnblogs.com/Maki-Nishikino/p/6217811.html

相關文章
相關標籤/搜索