看了很多大佬的部落格,每看一篇部落格懂一部分,總算是大概理解了樹狀陣列這個神奇又強大的東西;
在這裡我做個整合,把我認為好的部分摘錄下來;
參考部落格1:
參考部落格2:
下面是這兩位大佬寫的樂章合奏篇~~~~~~~~
首先,我們需要通篇以二進位制的視角來學習樹狀陣列,樹狀陣列就是應用二進位制的特點來簡化不必要的計算過程,利用位運算以實現高效的增刪改查;
說到樹狀陣列,顧名思義,這兩幅圖完全體現了它的核心思想;
首先我們搞明白樹狀陣列是用來幹嘛的,現在有乙個這樣的問題:有乙個陣列a
,下標從0
到n-1
,現在給你w
次修改,q
次查詢,修改的話是修改陣列中某乙個元素的值;查詢的話是查詢陣列中任意乙個區間的和;
這個問題很常見,首先分析下樸素做法的時間複雜度,修改是o(1
)'>o(1)
o(1)的時間複雜度,而查詢的話是o(n
)'>o(n)
o(n)的複雜度,總體時間複雜度為o(q
n)'>o(qn)
o(qn);可能你會想到字首和來優化這個查詢,我們也來分析下,查詢的話是o(1
)'>o(1)
o(1)的複雜度,而修改的時候修改乙個點,那麼在之後的所有字首和都要更新,所以修改的時間複雜度是o(n
)'>o(n)
o(n),總體時間複雜度還是o(q
n)'>o(qn)
o(qn)。
可以發現,兩種做法中,要麼查詢是o(1
)'>o(1)
o(1),修改是o(n
)'>o(n)
o(n);要麼修改是o(1
)'>o(1)
o(1),查詢是o(n
)'>o(n)
o(n)。那麼就有沒有一種做法可以綜合一下這兩種樸素做法,然後整體時間複雜度可以降乙個數量級呢?有的,對,就是樹狀陣列。
假設陣列a是我們增刪改查的物件,但樹狀陣列的思想維護的是 c 陣列, 從上面的圖我們可以看到,c[i]不是通常意義上的1~i 元素的和;
由圖來看看c陣列的規則,其中c8 = c4+c6+c7+a8,c6 = c5+a6……先不必糾結怎麼做到的,我們只要知道c陣列的大致規則即可,很容易知道c8表示a1~a8的和,但是c6卻是表示a5~a6的和,為什麼會產生這樣的區別的呢?或者說發明她的人為什麼這樣區別對待呢?答案是,這樣會使操作更簡單!看到這相信有些人就有些感覺了,為什麼複雜度被lg了呢?可以看到,c8可以看作a1~a8的左半邊和+右半邊和,而其中左半邊和是確定的c4,右半邊其實也是同樣的規則把a5~a8一分為二……繼續下去都是一分為二直到不能分,可以看看b圖。說白了樹狀陣列就是巧妙的利用了二分!
具體如何一分為二的規則通過 lowbit() 函式實現;
lowbit
這個函式的功能就是求某乙個數的二進位制表示中最低的一位1
,舉個例子,x = 6
,它的二進位制為110
,那麼lowbit(x)
就返回2
。
那麼怎麼求lowbit
呢?
兩種方法對應的**依次如下:
1intlowbit(x)
2
int更新操作:只要更新修改這個點會影響到的lowbit(x)
c
陣列,假設現在修改6(110)
這個點,依據樹狀陣列的性質三,它影響的直系父層就是c[6(110) + lowbit(6(110))] = c[8(1000)]
,但是它肯定不是只影響直系父層,上面所有包含這一層和的層都要更新,但是我們把這個更新傳遞給直系父層c[8]
,8
這個點的直系父層是c[16]
,依次類推地更新就行了;
1int sum(int x, arrayint c, intn)2
1void update(int x, int val, arrayint c, intn)2
POJ 2309 BST(樹狀陣列Lowbit)
題意是給你乙個滿二叉樹,給乙個數字,求以這個數為根的樹中最大值和最小值。理解樹狀陣列中的lowbit的用法。說這個之前我先說個叫lowbit的東西,lowbit k 就是把k的二進位制的高位1全部清空,只留下最低位的1,比如10的二進位制是1010,則lowbit k lowbit 1010 001...
樹狀陣列1 樹狀陣列入門
仔細看一下,發現tree的每乙個節點的高度並不是隨意的,而是由它轉成二進位制之後末尾連續零的數量決定的,連續零的數量加1,就是高度,例如 3 11 零的數量為0,加1等於1,所以它的高度就是1 6 110 零的數量為1,加1等於2,所以它的高度就是2 8 1000 零的數量為3,加1等於4,所以它的...
樹狀陣列 瞎bb 樹狀陣列
樹狀陣列是乙個利用一維陣列和位運算組成的求解區間問題的高效資料結構,其構造如圖所示 首先,我們要用它解決單點修改 區間查詢的操作。根據這張圖我們建立乙個陣列bit,下標就是圖中顯示的十進位制數。bit i 就表示了圖中所示的一段區間的和,例如bit 6 sum 5,6 bit 4 sum 1,4 下...