樹狀陣列詳解

2021-08-13 18:29:59 字數 2307 閱讀 8956

比如說,我這裡有一組數1, 2, 3, 2, …, k。我想知道第i到第j的和\(\mathop \sum \limits_^j v[i]\)是多少?

樸素演算法:

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

if (k >= i && k <= j) ans += v[k];

類似這種的寫法,雖然在某些點值改變時也依然可以計算(我們稱這種問題為動態問題),但複雜度最高到o(n),實在難以接受。

樹狀陣列是通過字首和思想,用來完成單點更新和區間查詢的資料結構。它比之線段樹,所用空間更小,速度更快,而且程式設計的複雜難度也大大減小。和st相比,它可以動態更新資料。

我們可以把查詢區間和問題轉化為字首和問題。

字首和是指:v[1]~v[i]的和,記作sum(i)。

如果我們能夠計算s和t的字首和,用sum(t)−sum(s−1)可以得到區間[s, t]的和。

而樹狀陣列正是乙個支援求字首和的資料結構。

(如果你學過線段樹,可以先從最後「樹狀陣列的缺陷」看起,樹狀陣列的靈感**脈絡會更加清晰)

我們依據二進位制的思想,讓」為1的最低位」在第一位的數(即奇數),儲存長度為1的區間和(即v[i])。最低位在第二位的數,儲存長度為2的區間和(v[i-1]+v[i])。這樣,value陣列儲存了大大小小、長度為2^n 的區間和。

每乙個長條形的塊表示這個節點覆蓋的區間。value[index]等於區間節點值之和。

這樣做有什麼好處呢?

1. 任意字首和sum(i)可以通過加運算得到。

2. 任意v[i]都可以通過減運算得到。

注:後面出現的節點編號都是二進位制數

- 如何求「最低位在哪一位」

舉乙個具體的運算過程:

運算元值

表示式給定i

10010

ii取反

01101

~i加一

01110

~i+1

和i求交

00011&

通過上面4步就可以求乙個數的最低位在哪一位了!

我們先由此定義乙個lowbit函式:lowbit(i) = 保留i最低位的1和更低位的0。

比如lowbit(100010) = 10

那麼我們就可以具體推導出一些性質了:

1. 區間長度為1<

2. 區間是[i-lowbit(i)+1, i]

在具體程式中,lowbit(i) = i & -i。因為計算機裡的整數採用補碼表示,因此-x是x取反後+1的結果。

而對於節點編號為0110的節點,它恰好由乙個長度為4的區間和乙個長度為2的區間組成。第乙個lowbit(i)表示它自身所覆蓋的區間,而第二次lowbit(i-lowbit(i))表示它的前乙個區間。同理,我們可以遞迴地求得所有它前面的區間。

為了求得sum(i),將i通過二進位制分解,不斷讓i = i - lowbit(i)。對於每個求得的i(包括初始i)求和value[i]就是sum(i)

和線段樹的更新不同,bit的更新指的是v[i]加上a。所以需要讓x[i] = a時,需要先-v[i]再+a。

對於更新操作,因為我們在節點儲存部分和,所以當乙個點更新後,覆蓋這個點的父節點的值也要更新。我們的問題在於如何找到它的父節點,父節點的父節點等等,一直更新到頂部。

根據之前的鋪墊,我們對bit已經有了一定的認識,我們可以感性地猜想,乙個節點的父節點t = i + lowbit(i)。想法是基於:通過進製使得覆蓋的區間變大,而且可以證明, t - lowbit(t) + 1 <= i < t。i一定會被覆蓋。

同理遞迴地更新上去就可以了。

事實上,我們更新的點都是2的冪,即形如1000的。所以每次加上lowbit,讓低位全為0就可以。

求和

ll b[max_n+1];

ll sum(int i)

return s;

}

更新

void add(int i, ll v) 

}

其實談到樹狀陣列,有乙個資料結構不能不提——線段樹。

線段樹的每個節點都儲存了資料,但在計算和和更新值時,我們是不會用到右兒子的!如上圖所示。

於是我們刪除了線段樹的所有右兒子,形成了樹狀陣列。

但是這裡有乙個前提條件,也是樹狀陣列的缺陷:節點的資料必須是可加減,而且要滿足a + b = c, c -a = b

比如集合運算,是不能通過父節點和左子節點 來計算右子節點的。

i. 《挑戰程式設計競賽 第二版》

ii. 《演算法競賽 入門經典 訓練指南》

這裡是我的blog:有更多演算法分享。排版可能也會更好看一點=v=

樹狀陣列 詳解

對於普通陣列,其修改的時間複雜度位o 1 而求陣列中某一段的數值和的時間複雜度為o n 因此對於n的值過大的情況,普通陣列的時間複雜度我們是接受不了的。在此,我們引入了樹狀陣列的資料結構,它能在o logn 內對陣列的值進行修改和查詢某一段數值的和。假設a陣列為儲存原來的值得陣列,c為樹狀陣列。我們...

樹狀陣列詳解

樹狀陣列求區間和的一些常見模型 樹狀陣列在區間求和問題上有大用,其三種複雜度都比線段樹要低很多 有關區間求和的問題主要有以下三個模型 以下設a 1.n 為乙個長為n的序列,初始值為全0 1 改點求段 型,即對於序列a有以下操作 修改操作 將a x 的值加上c 求和操作 求此時a l.r 的和。這是最...

樹狀陣列 詳解

由圖可知,原始的陣列是a陣列,樹狀陣列是e陣列。通俗的說 e 1 a 1 e 2 a 1 a 2 e 3 a 3 e 4 a 1 a 2 a 3 a 4 e 5 a 5 e 6 a 5 a 6 e 7 a 7 e 8 a 1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 為什麼是這樣的規律...