c c 樹狀陣列 入門

2021-08-18 03:54:24 字數 3295 閱讀 9645

樹狀陣列是十分的優雅的結構,用於解決區間求和,單點修改,樹狀陣列和線段樹很相似 ,線段樹的使用範圍更廣,樹狀陣列雖然可用的範圍比線段樹小但是它的效率比線段樹高 

下面就是樹狀陣列的基本圖形,首先要說明的是樹狀陣列是個一維的陣列 ,樹狀陣列的下標是從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,最後輸出整個陣列每個元素的值。更新區間...