1. 程式人生 > >數據結構-樹狀數組(一)

數據結構-樹狀數組(一)

stream owb 數組長度 using lan alt 存儲 單點 info

復習筆記:樹狀數組(一)

基本原理

樹狀數組,顧名思義,是一個存儲方式像樹一樣的數組。它只需要開和原數組一樣大小的內存,但是每個數的位置存的並不是每個數的原始值,而是像這樣:

技術分享圖片(引用自度娘)

或者用數據來說,假設原數組為A[N],樹狀數組為C[N],那麽存儲方式就像下面這樣

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 ... C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 + A9 + A10 + A11 + A12 + A13 + A14 + A15 + A16 這就是樹狀數組的存儲方式。

樹狀數組的優勢

可以很簡單實現區間修改單點查詢、區間查詢單點修改、

例:輸出數組x到y的區間和

暴力模擬:

1 int sum = 0;
2 for(int i = x;i <= y;i++)
3     sum += a[i];

很明顯時間復雜度是O(n),當數據量大並且需要多次查詢,難免TLE,樹狀數組可以解決這個問題

程序實現

性質:

設節點編號為x,那麽這個節點管轄的區間為2^k(其中k為x二進制末尾0的個數)個元素。因為這個區間最後一個元素必然為Ax,所以很明顯:Cn = A(n – 2^k + 1) + ... + An。 所以,當需要修改第x個數時,需要把所有管轄x的節點進行修改。這裏就需要用到了一個很巧妙的節點轉移量:Lowbit。lowbit(x)=x&(-x)。所以程序實現是這樣

節點轉移函數:lowbit

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

甚至可以是這樣

1 #define lowbit(x) x&-x

單點修改

在數組長度n的範圍內,從x開始,每次修改完,下一個要修改的數就是當前修改數的lowbit值,這樣以此類推,就可以把管轄著x的點全部修改完,也就是這樣

1 void add(int x,int k){
2     while(x <= n){
3         tree[x] += k;
4         x += lowbit(x)
5 }

這就是把第x個點加上k的操作(tree為樹狀數組)

區間查詢

根據樹狀數組的存儲方式,將單點修改的程序逆過來,也就是在>=1的範圍內,從x開始每次減去當前下標lowbit,並將節點權加入到總和,就可以求出A1+A2...+Ax(A為原數組值),也就是第x個數的前綴和。

 1 int sum(int x)
 2     {
 3         int ans = 0;
 4         while(x != 0)
 5         {
 6             ans += tree[x];
 7             x -= lowbit(x);
 8         }
 9         return ans;
10     }

那麽想要求出x到y的區間和,只需要用Ay的前綴和減去Ax-1的前綴和即可,就像這樣

1 int search(int x,int y){
2     return sum(y) - sum(x - 1);
3 }

但大多數情況下不需要寫函數,直接寫到主程序裏。

例題:P3374

大意:實現區間查詢,單點修改

第一行包含兩個整數N、M,分別表示該數列數字的個數和操作的總個數。

第二行包含N個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。

接下來M行每行包含3個整數,表示一個操作,具體如下:

操作1: 格式:1 x k 含義:將第x個數加上k

操作2: 格式:2 x y 含義:輸出區間[x,y]內每個數的和

程序就是這樣:

 1     #include <iostream>
 2     #include <cstdio>
 3     #include <algorithm>
 4     #include <cmath>
 5     #include <cstring>
 6     using namespace std;
 7     int n,m,tree[2000010];
 8     int lowbit(int k)
 9     {
10         return k & -k;
11     }
12     void add(int x,int k)
13     {
14         while(x<=n)
15         {
16             tree[x]+=k;
17             x+=lowbit(x);
18         }
19     }
20     int sum(int x)
21     {
22         int ans=0;
23         while(x!=0)
24         {
25             ans+=tree[x];
26             x-=lowbit(x);
27         }
28         return ans;
29     }
30     int main()
31     {
32         cin>>n>>m;
33         for(int i=1;i<=n;i++)
34         {
35             int a;
36             scanf("%d",&a);
37             add(i,a);
38         }
39         for(int i=1;i<=m;i++)
40         {
41             int a,b,c;
42             scanf("%d%d%d",&a,&b,&c);
43             if(a==1)
44                 add(b,c);
45             if(a==2)
46                 cout<<sum(c)-sum(b-1)<<endl;
47         }
48     }

數據結構-樹狀數組(一)