比如說,我這裡有一組數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 為什麼是這樣的規律...