樹狀陣列的學習

2022-05-06 15:42:11 字數 4026 閱讀 9058

學習部落格:

樹狀陣列(binary indexed tree(b.i.t), fenwick tree)是乙個查詢和修改複雜度都為

log(n)

的資料結構。主要用於查詢任意兩位之間的所有元素之和,但是每次只能修改乙個元素的值;經過簡單修改可以在

log(n)

的複雜度下進行範圍修改,但是這時只能查詢其中乙個元素的值

(如果加入多個輔助陣列則可以實現區間修改與區間查詢)。

樹狀陣列和線段樹很像,但能用樹狀陣列解決的問題,基本上都能用線段樹解決,而線段樹能解決的樹狀陣列不一定能解決。相比較而言,樹狀陣列效率要高很多。

通過二進位制,將陣列的節點序號都轉化成二進位制。

就是這樣的:

1--->001     c1 = a1

2--->010     c2 = a1 + a2

3--->011     c3 = a3

4--->100     c4 = a1 + a2 + a3 + a4

5--->101     c5 = a5

6--->110     c6 = a5 + a6     

7--->111     c7 = a7

8--->1000   c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8

但是這又有什麼關係呢?

這裡有乙個有趣的性質:

設節點編號為x,那麼這個節點管轄的區間為

2^k(其中k為

x二進位制末尾

0的個數)個元素。因為這個區間最後乙個元素必然為ax,

所以很明顯:cn = a(n – 2^k + 1) + a(n-2^k+2)+... + an

什麼意思呢?舉個栗子,1對應的二進位制是

001,二進位制末尾

0的個數為

0,所以

k=0,所以

c1=a(1-2^k+1)+...an

就是c1=a(1-2^0+1)=a(1);

再來個栗子,4對應的二進位制是

100,二進位制末尾

0的個數為

2,所以

k=2,所以

c4=a(4-2^2+1)+a(4-2^2+2)+...+a(4)=a(1)+a(2)+a(3)+a(4);

再搞不懂就自己再手推幾個數試試就可以了。

至於怎麼得出來的這個神奇的東西,就是靠

二進位制。

接下來就是怎麼**實現這個呢?

就是要靠神奇的lowbit了。

int lowbit(int x)

{return x&(-x);

x&(-x)就是整數

x與其相反數

(負號取反

)的按位與

:相同位的兩個數字都為

1,則為

1;若有乙個不為

1,則為0,即

:1&1=1,0&1=0,0&0=0;

計算機中負數使用對應正數的補碼來表示。

-x就是

x對應的二進位制數先各位取反,0變成

1,1變成

0。然後最低位加1。

舉個栗子,4對應二進位制為

100;

-4對應的為

011+1=100

,所以為

(100)&(100)

所以為100

。知道這個就可以進行區間查詢啦。

區間查詢

利用c[i]求

a陣列前

i個的和;

//**1:

int sum(int n)

{int s=0;

while(n>0){

s+=c[n];

n-=lowbit(n);

return s;

//**2:

int sum(int n)

{int s=0;

for(int i=n;i>0;i-=lowbit(i))

s+=c[i];

return s;

兩個**哪個好理解就理解哪個。

接著剛剛舉的栗子4,求

a陣列前

4個數的和;

lowbit(4)得出

100;然後

100就是

4,所以為

s+=c[4];

此時i=4,4-lowbit(4)=100-100=0;

結束。再舉個栗子7,求前

7個數的和,就是

s+=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7];

因為1--->001     c1 = a1

2--->010     c2 = a1 + a2

3--->011     c3 = a3

4--->100     c4 = a1 + a2 + a3 + a4

5--->101     c5 = a5

6--->110     c6 = a5 + a6     

7--->111     c7 = a7

8--->1000  c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8

所以為c[4]+c[6]+c[7];

就是c[100]+c[110]+c[111];

首先s+=c[7];

然後lowbit[7]=(111)&(001)=001;此時

i=7,

所以為7-lowbit(7)=111-001=110,110

就是6;s+=c[6];

lowbit(6)=(110)&(010)=010;此時

i=6,

所以為6-lowbit(6)=110-010=100,100

就是4;s+=c[4];

lowbit(4)=(100)&(100)=100;此時

i=4,

所以為4-lowbit(4)=100-100=0,

結束。所以得到a陣列中前

7個數的和為

c[7]+c[6]+c[4];

不懂的話再自己手推乙個數就差不多懂了。

接下來就是單點更新。

單點更新

要從下往上依次更新。

//**1:

void add(int x)

{while(x<=n){

++a[x];

x+=lowbit(x);

//**2:

void add(int x,int y)

{for(int i=x;i<=n;i+=lowbit(i))

c[i]+=y;

更新過程仔細想一想就是查詢過程的逆過程;

舉個栗子,更新a1,還要繼續更新

c[1],c[2],c[4],c[8];

就是c[001],c[010],c[100],c[1000];

i=1;c[1]+=a[1];

lowbit(1)=(001)&(001)=001;此時

i=1,

所以為1+lowbit(1)=001+001=010,010

就是2;c[2]+=a[1];

lowbit(2)=(010)&(110)=010;此時

i=2,

所以為2+lowbit(2)=010+010=100,100

就是4;c[4]+=a[1];

lowbit(4)=(100)&(100)=100;此時

i=4,

所以為4+lowbit(4)=100+100=1000,1000

就是8;c[8]+=a[1];

結束。

樹狀陣列學習

之前寫的題也遇到過用樹狀陣列,當時都是現查現學,而且總是搞不懂,今天又遇到了一道求區間和的題,不管最後是不是用樹狀陣列可以a,但是既然已經想到了這,就打算好好學習一下。可惜之前查到的資料都沒有儲存記錄,所以又重新查了些資料,彙總學習如下 文末附上樹狀陣列的詳細 樹狀陣列主要用到的操作 int low...

樹狀陣列學習

我覺得,樹狀陣列挺重要的就是那個 lower x x x 我說說我的理解吧。每乙個正整數都可以拆分成 2 的某些冪之和,例如 15 8 4 2 1 6 4 2 7 4 2 1 感覺可以解釋lca的倍增跳 那麼轉換成二進位制是什麼樣的呢?15 十進位制 1111 1000 100 10 1 6 十進位...

樹狀陣列學習

樹狀陣列與並查集類似,是一種資料結構,它可以用來維護字首和問題 不過,利用字首和和查分的思想,我們也可以用樹狀陣列解決區間問題 與線段樹不同,目前我暫且認為線段樹解決最值問題,而樹狀陣列解決求和問題 樹狀陣列原理建立在二叉樹上 利用lowbit運算實現向根節點儲存的原理 介紹lowbit的程式實現只...