樹狀陣列是十分的優雅的結構,用於解決區間求和,單點修改,樹狀陣列和線段樹很相似 ,線段樹的使用範圍更廣,樹狀陣列雖然可用的範圍比線段樹小但是它的效率比線段樹高
下面就是樹狀陣列的基本圖形,首先要說明的是樹狀陣列是個一維的陣列 ,樹狀陣列的下標是從1開始的 而不是從0開始的,我們只是利用了它的下表值的特點,來進行區間的求和,單點修改。
1 紅色標註的是現在 樹狀陣列的下標
2 每個紅色標註的上方的方格內代表著樹狀陣列能表示原來的陣列( 就是要求和,修改單點的陣列 )
比如 紅色下標 2 上方的 (1,2) 表示了他代表的是原來陣列從 a[1] 一直到a[2]的和 (假設原來的陣列為 a[8])
再比如 紅色下標3 上方的 (3) , 就表示他代表的是原來陣列 a[3] (只有乙個就不用再說求和了。。。)
看下面這個** ,要注意的是 樹狀陣列巧妙地利用了 二進位制數的特點 。
這個圖和上邊的 圖是相互對應的 ,觀察到一下的特點
1 . 節點下表和第四列的 最後乙個元素是一樣的 (就是樹狀陣列的乙個節點 所表示的範圍的最後乙個元素)。
2. 節點對應的元素個數是 節點下標的二進位制中從右到左的0的個數 的二次方,也就是從右到左有k個0,那麼能表示的
個數就是2^k , 比如1的二進位制 k =0,所以它能表示的個數是2^0 (就是1個). 再比如6的二進位制 0110 , k = 1所以它能表示的個數就是 2個。
3 . 每個節點所能表示的原來陣列( 就是要求和,修改單點的陣列 )的下標都是連續的。
一 . 在了解樹狀陣列的這些特性之後我們來看一下 樹狀陣列很重要的 lowbit() 這個函式
int lowbit(int x)
下面這個證明說的很詳細
首先明白乙個概念,計算機中-i=(i的取反+1),也就是i的補碼
而lowbit,就是求(樹狀陣列中)乙個數二進位制的1的最低位,例如01100110,lowbit=00000010;再例如01100000,lowbit=00100000。
所以若乙個數(先考慮四位)的二進位制為abcd,那麼其取反為(1-a)(1-b)(1-c)(1-d),那麼其補碼為(1-a)(1-b)(1-c)(2-d)。
如果d為1,什麼事都沒有-_-|||但我們知道如果d為0,天理不容2σ( ° △ °|||)︴
於是就要進製。如果c也為0,那麼1-b又要加1,然後又有可能是1-a……直到碰見乙個為補碼為0的bit,我們假設這個bit的位置為x
這個時候可以發現:是不是x之前的bit的補碼都與其自身不同?,x之後的補碼與其自身一樣都是0?
例如01101000,反碼為10010111,補碼為10011000,可以看到在原來數正數第五位前,補碼的進製因第五位使其不會受到影響,於是0&1=0,;
但在這個原來數「1」後,所有零的補碼都會因加1而進製,導致在這個「1」後所有數都變成0,再加上0&0=0,所以他們運算結果也都是零;
只有在這個數處,0+1=1,連鎖反應停止,所以這個數就被確定啦o(∩_∩)o
所以and以後只有x這個bit是一……
二 . 知道了lowbit . 在看 樹狀陣列的就和的函式 getsum ,樹狀陣列為a[8], 要注意的是樹狀陣列的大小和原來陣列的大小是一樣的。 原型如下:
int getsum(int x)
return s; }
可能有人不太明白getsum 為什麼 只求每次下標為 x 減去lowbit(x) 的和,可以看一下上面的圖
比如要求原來陣列的前6項和,也就是 getsum(6);6 的二進位制是 0110 吧 ,帶入getsum中
在第一次進入到while迴圈的時候 s = s+ a[6], 可以看上面的圖 a[6] = a[5]+a[6]; 也就是s 現在等於a[5] + a[6]了 ,然後
x = x-lowbit(x), 希望大家了解了lowbit(x)的作用, lowbit(6) = 0010 , 原來的 6是0110, 這樣 6 - lowbit(6) = 010 0 ,也就是x = 0100 = 4;
然後x > 0 , 第二次進入while , 這時候 s = s + a[4] ,看上圖的 a[4] = a[1] + a[2] + a[3] + a[4], 這樣s = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] , 然後 x = x - lowbit(x), lowbit(4) = 0100 , 4 = 0100, 4 - lowbit(4) = 0; 所以x = 0 然後 就不再進入while , 也求出了 getsum(6), 這就是getsum()的原理。
三. 然後就是另外乙個 函式 update() ,原型如下
void update(int x,int num)
這個函式的作用就是當修改了 原來乙個陣列的乙個值的時候,通過這個函式來維護樹狀陣列的值,樹狀陣列的時間複雜度是 o(log n) , 因為樹狀陣列是圍繞這二進位制進行修改的 n的二進位制最多是 log n 個 所以複雜度為log n ,
update()的原理和 getsum()是一樣的 ,當你修改了 原來陣列的a[3]吧 你就要修改 所有包括a[3]的樹狀陣列,而尋找包含a[3]的樹狀陣列就是通過 x += lowbit(x) , 同樣觀察上邊的圖就可以看出來,
敵兵布陣
#include #include #include #include using namespace std;
const int maxn = 50010;
int a[maxn];
int n;
int t;
int ot[maxn];
int lowbit(int x)
void update(int x,int num)
}int getsum(int x)
return sum;
}int main()
char ch[10];
int i,j;
getchar();
scanf("%s",ch);
int ans = 0;
while(strcmp(ch,"end"))
cout<
for(int i = 0; i < ans ; i++)
cout<
}return 0;
}
樹狀陣列1 樹狀陣列入門
仔細看一下,發現tree的每乙個節點的高度並不是隨意的,而是由它轉成二進位制之後末尾連續零的數量決定的,連續零的數量加1,就是高度,例如 3 11 零的數量為0,加1等於1,所以它的高度就是1 6 110 零的數量為1,加1等於2,所以它的高度就是2 8 1000 零的數量為3,加1等於4,所以它的...
樹狀陣列 入門
樹狀陣列,乙個用來區間求和修改都為log n 的演算法。在網上資料很多,看起來也不是很難,學習一下!基本上學習樹狀陣列都會看到下面這個圖,這也是最基本的乙個圖,看懂這個以後,對樹狀陣列也就會有 一定的了解了。令這棵樹的結點編號為c1,c2.cn。令每個結點的值為這棵子樹的值的總和,那麼容易發現 c1...
樹狀陣列入門
用office做了一張pdf 這是一維的情形,如果是二維,可以把每一行的一維樹狀陣列看成乙個節點,然後再把二維樹狀陣列看成一維樹狀陣列。好文章 兩道入門題 對於第一題hdu1556 題意 初始陣列元素值都為0,給定區間 x,y 將區間 x,y 每個元素的值 1,最後輸出整個陣列每個元素的值。更新區間...