樹狀陣列 小白演算法學習

2021-08-06 00:14:09 字數 2491 閱讀 6834

在解題過程中,我們有時需要維護乙個陣列的字首和s[i]=a[1]+a[2]+...+a[i]。

但是不難發現,如果我們修改了任意乙個a[i],s[i]、s[i+1]...s[n]都會發生變化。

可以說,每次修改a[i]後,調整字首和s在最壞情況下會需要o(n)的時間。

當n非常大時,程式會執行得非常緩慢。

因此,這裡我們引入「樹狀陣列」,它的修改與求和都是o(logn)的,效率非常高。

為了對樹狀陣列有個形象的認識,我們先看下面這張圖。

如圖所示,紅色矩形表示的陣列c就是我們所維護的樹狀陣列。

該影象的特徵:構成了乙個以c[8]為根結點,其他c[m]、a[n]為子結點的樹。

對於陣列a下標為奇數時,其上都僅僅只有乙個c元素,即c[1] = a[1]、c[3] = a[3]、c[5] = a[5]等等

對於陣列a下標為偶數時,這是一系列a[i]的和。

c[1] = a[1];c[1]的管轄範圍為1

c[2] = a[2] + a[1] ;c[2]的管轄範圍為2

c[3] = a[3];c[3]的管轄範圍為1

c[4] = a[4] + a[3] + a[2] + a[1] ;c[4]的管轄範圍為4

c[5] = a[5];c[5]的管轄範圍為1

c[6] = a[6] + a[5] ;c[6]的管轄範圍為2

c[7] = a[7];c[7]的管轄範圍為1

c[8] = a[8] + a[7] + a[6] + a[5] + a[4] + a[3] + a[2] + a[1] ;c[8]的管轄範圍為8

這裡的管轄範圍就是說c由幾個a組成。

由上可得(s由c來表示):

s[1] = c[1]

s[2] = c[2]

s[3] = c[3] + c[2]

s[4] = c[4]

s[5] = c[5] + c[4]

s[6] = c[6] + c[4]

s[7] = c[7] + c[6] + c[4]

s[8] = c[8]

通過觀察影象:s[i]表示為i之前所有最大子樹的根結點c累加的和

上面所說的管轄範圍就是下面列出來的2^k所表示的數

i                   

二進位制                 

2^k(k:i的二進位制的末尾0的個數)     101

12102

31114

1004

510116

1102

711118

1000

82^k的求法:i 的二進位制去掉所有高位的1,只保留最低位的1,如果只存在乙個最高位的1,則 2^k 就是它本身。

如1(1),2(10),4(100),8(1000),16(10000)。即都是2的冪。

所以,當我們要求2的冪是只要i等於2^k就可以了,而2^k由位運算很容易求得。

對於c[i]如何求得他的父節點?

由上**推斷,c[i]的父節點為c[i+2^k],也就是說將 i 的二進位制的最末尾的1去掉,相鄰的高位+1

對於c[i]如何求得他的下乙個節點?

由上**推斷,c[i]的下乙個節點是c[i-2^k],也就是說將i的二進位制最末尾的1去掉。

由上:c[i]表示a[i-2^k+1]到a[i]的和,而k則是i在二進位制時末尾0的個數。

利用位運算,我們可以直接計算出2^k=i&(i^(i-1)) 也就是i&(-i),所以c[i]的父節點為c[i + i & (-i)]。

這個k(i的二進位制末尾0的個數)就是該節點在樹中的高度,因而這個樹的高度不會超過logn。

所以,當我們修改a[i]的值時,可以從c[i]往根節點一路上溯,調整這條路上的所有c即可,這個操作的複雜度在最壞情況下就是樹的高度即o(logn)。  

對於求數列的前n項和s[i],只需找到n以前的所有最大子樹,把其根節點的c加起來即可。

不難發現,這些子樹的數目是n在二進位制時1的個數。因此,求和操作的複雜度也是o(logn)。

在最後,我們將給出一些樹狀陣列的實現**,希望讀者能夠仔細體會其中的細節。

求最小冪2^k:

[cpp]view plain

copy

intlowbit(

intt)   

求前n項和:

[cpp]view plain

copy

intsum(

intend)   

return

sum;   

}   

對某個元素進行加法操作: 

[cpp]view plain

copy

void

plus(

intpos , 

intnum)   

}   

參考:

演算法學習筆記 樹狀陣列

樹狀陣列 binary indexed trees 是一種可以支援單點修改,較快維護字首和的資料結構。他的實現方式是用乙個陣列維護乙個 樹狀 的結構 如下圖所示 記錄一些區間的區間和,實現快速計算字首和。能看到這裡的同學應該已經對字首和不陌生了。本片部落格就不再贅述 lowbit 操作是表達二進位制...

演算法學習筆記 樹狀陣列 線段樹

支援單點修改和區間查詢兩個操作,單次操作o logn tr x 陣列存的是原陣列在區間 x lowbit x x 上的和 若要將乙個數x變為v,則將x x v,即加上v x 模板如下 建立樹狀陣列 for int i 1 i n i scanf d a i for int i 1 i n i add...

樹狀陣列 小白

樹狀陣列 bit 是一種利用樹的2進製特徵進行檢索的樹狀結構。樹狀結構是一種奇妙的資料結構,不僅非常高效,而且 十分簡潔 比線段樹的 要短且更易理解,但是可以解決的問題也是有限的,沒有線段樹那麼廣泛 樹狀陣列就是用來 動態的求字首和 的時間複雜度在log n之內 一般來說就是兩個操作 1.單點修改 ...