樹狀陣列,乙個用來區間求和修改都為log(n)的演算法。在網上資料很多,看起來也不是很難,學習一下!
基本上學習樹狀陣列都會看到下面這個圖,這也是最基本的乙個圖,看懂這個以後,對樹狀陣列也就會有
一定的了解了。
令這棵樹的結點編號為c1,c2...cn。令每個結點的值為這棵子樹的值的總和,那麼容易發現:
c1 = a1
c2 = a1 + a2
c3 = a3
c4 = a1 + a2 + a3 + a4
c5 = a5
c6 = a5 + a6
c7 = a7
c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
c16 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16
設節點編號為x,那麼這個節點管轄的區間為2^k(其中k為x二進位制末尾0的個數)個元素。因為這個區間最後乙個元素必然為ax,
所以很明顯:cn = a(n – 2^k + 1) + ... + an
算這個2^k有乙個快捷的辦法,定義乙個函式如下即可:
int lowbit(int x)
return x&(x^(x–1));
當想要查詢乙個sum(n)(求a[n]的和),可以依據如下演算法即可:
step1: 令sum = 0,轉第二步;
step2: 假如n <= 0,演算法結束,返回sum值,否則sum = sum + cn,轉第三步;
step3: 令n = n – lowbit(n),轉第二步。
可以看出,這個演算法就是將這乙個個區間的和全部加起來,為什麼是效率是log(n)的呢?以下給出證明:
n = n – lowbit(n)這一步實際上等價於將n的二進位制的最後乙個1減去。而n的二進位制裡最多有log(n)個1,所以查詢效率是log(n)的。
那麼修改呢,修改乙個節點,必須修改其所有祖先,最壞情況下為修改第乙個元素,最多有log(n)的祖先。
所以修改演算法如下(給某個結點i加上x):
step1: 當i > n時,演算法結束,否則轉第二步;
step2: ci = ci + x, i = i + lowbit(i)轉第一步。
i = i +lowbit(i)這個過程實際上也只是乙個把末尾1補為0的過程。
對於陣列求和來說樹狀陣列簡直太快了!
實現**:
樹狀陣列是乙個很高效的進行區間統計的資料結構,在思想上類似於線段樹,相比的話,空間複雜度低,程式設計複雜度低,可擴張到多維。但缺點是適用範圍小。
樹狀陣列1 樹狀陣列入門
仔細看一下,發現tree的每乙個節點的高度並不是隨意的,而是由它轉成二進位制之後末尾連續零的數量決定的,連續零的數量加1,就是高度,例如 3 11 零的數量為0,加1等於1,所以它的高度就是1 6 110 零的數量為1,加1等於2,所以它的高度就是2 8 1000 零的數量為3,加1等於4,所以它的...
樹狀陣列入門
用office做了一張pdf 這是一維的情形,如果是二維,可以把每一行的一維樹狀陣列看成乙個節點,然後再把二維樹狀陣列看成一維樹狀陣列。好文章 兩道入門題 對於第一題hdu1556 題意 初始陣列元素值都為0,給定區間 x,y 將區間 x,y 每個元素的值 1,最後輸出整個陣列每個元素的值。更新區間...
樹狀陣列入門
樹狀陣列 模板 include include includeusing namespace std const int maxn 1e6 10 int c maxn int lowbits int x void update int x,int y,int n int getsum int x r...