樹狀數組

什麼是樹狀數組???

樹狀數組就是把一個一般的數組弄成一個像樹一樣的結構!

如圖:(圖片來自百度)


        剛開始是數組A,經過變換後,C[1]=A[1], C[2]=A[1]+A[2],C[3]=A[3],C[4]=A[1]+A[2]+A[3]+A[4] 等等;

爲什麼要把好好的數組變成這樣呢?

        這要從樹狀數組所要解決的問題說起,樹狀數組所要解決的就是求數組某一個區間的和的問題,如果不用樹狀數組,而用一般的方法,解決這樣的問題,我們就要在給定的區間中循環累加,需要求幾個區間的和就必須循環幾次,顯然,如果數據比較多,這種做法耗費的時間肯定比較多。

        我們有可能還會想到另一種方法,就是把該數組中的各個區間的和先求出來,存放到另一個數組中,這樣求區間和的時候就可以直接使用,但是用這種方法還用一個問題,就是當我們改變數組中的某個值時,存放區間和的那個數組就需要很大的改動,比如:兩個數組A[100](原數組) , B[100](存放區間和的數組) ,如果A[4]的值改變了,那麼在B數組中 A[1]+A[2]+A[3]+A[4] , A[2]+A[3]+A[4] , A[3]+A[4], A[4]+A[5] ,A[4]+A[5]+A[6]等等和A[4]有關的和都要改變,這就增加了時間的複雜度。

        爲了解決這些問題,樹狀數組應運而生了,如上圖所示這種數據結構也是把數組的區間和存到數組中(可以是本數組),但是這些區間和存放的是一個樹形的結構,弄成樹形結構的目的是什麼呢?弄成樹形結構的目的就是解決改變數組中的值得時候能夠減少區間和的更新次數。

樹狀數組的思想說完了,接下來說一下它的原理:

根據上圖人們總結出了一個規律:

C[1]=A[1] ;

C[2]=A[1]+A[2];

C[3]=A[3];

C[4]=A[1]+A[2]+A[3]+A[4];

C[5]=A[5];

C[i]= ?;

這些式子有什麼規律呢?C[i]又等於多少呢?

        一些牛人發現,C[i]等於多少,完全取決於 i 等於幾,也就是我們把 i 轉換爲二進制,如2 = (10)(!從末尾向前查有1個0),那麼C[2]後面就應該有2的一次方個元素,也就是2個元素,那究竟是從哪個元素到哪個元素呢? 牛人們又發現元素區間和 i 還有關係,如C[2] = A[2-(2的k次方)+1]......A[2]  (k代表2轉換成2進制後從後向前查有多少個0)。

總結:C[i] 所代表的區間和爲:A[i-2的k次方+1].......A[i];(k和上面的意義一樣);

怎麼弄求出k呢?是不是想着先把 i 轉換成2進制,然後存放到數組中,然後從後向前遍歷,求出0的個數。

其實又有一些牛人替我們解決了這個問題,人家只用一段很短的代碼就直接求出了2的k次方:

一,int liw(int i)

{

return i&(-i);

}

二,int liw(int i)

{

return i&(i^(i–1));

}

兩種方法結果都一樣,都是求出2的k次方!

怎麼求前 i 項的和呢?

int sum(int i)

{

int s=0;

while(i>0)

{

s=s+C[i];

i=i-liw(i);//這句代碼可以看圖自己手寫運行一遍

}

}

最後就是更新數組的值了。

void add(int  i , int  v)//對數組中第i個元素加v

{

while(i<=m)//m是數組的長度

{

C[i]=C[i]+v;

i=i+liw(i);//自己對照上圖運行

}

}

樹狀數組的核心思想差不多就是這麼多,下面有兩個題,幫助大家理解樹狀數組!

一. 給定一個數組,求數組中某個區間的和。

輸入:m,n 分別表示數組中的元素個數,和區間數;

輸出:每個區間的和;

#include<stdio.h>

int m,n;

int c[1000]={0};

int liw(int i)

{

return i&(-i);

}

void add(int  i , int  v)//對數組中第i個元素加v

{

while(i<=m)//m是數組的長度

{

C[i]=C[i]+v;

i=i+liw(i);//自己對照上圖運行

}

}

int sum(int i)

{

int s=0;

while(i>0)

{

s=s+C[i];

i=i-liw(i);//這句代碼可以看圖自己手寫運行一遍

}

}

int main(void)

{

int s,con,x,y;

for(int i=1;i<=m;i++)

{

scanf("%d",&s);

add(i,s);//在此是創建樹狀數組,因爲剛開始數組元素爲0

}

for(int k=1;k<=n;k++)

{

scanf("%d%d",&x,&y);

con=sum(y)-sum(x-1);

printf("%d\n",con);

}

return 0;

}

這是最簡單的樹狀數組的應用!

更多的應用可以看nyoj《士兵殺敵》問題!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章