1. 程式人生 > >樹狀陣列 Binary Indexed Tree 學習筆記

樹狀陣列 Binary Indexed Tree 學習筆記

看了不少樹狀陣列的資料,講解大多十分清楚,但是都沒有明確的推導過程,所以自己嘗試不那麼嚴謹地推導一下

一個長度為n的陣列C,需要能修改任意位置的數,並求前k個數的和(或者連續k個數的和)

  • 如果直接累加,則修改時間複雜度O(1),求和時間複雜度O(n)
  • 如果使用輔助陣列儲存前k個數的和,則修改時需要更新從當前位置到陣列末尾的所有數,時間複雜度O(n),求和時間複雜度O(1)

考慮平衡修改複雜度和求和複雜度,除非儲存前k個數的和,否則求和複雜度不可能降為O(1),所以考慮想辦法降為O(log(n))

  • 如果生成輔助陣列D,將前k個數求和化作k的二進位制形式每個1代表的數在輔助陣列D中的值的和,則求和複雜度可以降為O(log(n))

考慮如何實現上述方案,即求輔助陣列D

  • 令陣列C=(C0,C1,...,Cn-1),輔助陣列D=(D0,D1,...,Dn),D0=0
  • 在k>0的情況下,設i=f(k)為k的二進位制表示的從右數第一個1的位置(從0開始計算),則D[k]其實是包含從C[k-1]到C[k-2i],共2i個數的和
  • 由於k二進位制表示從右數i右邊的位置都是0,可知D[k]其實是對滿足如下條件的所有C[j]求和:j二進位制形式低i位從000...000、000...001、000...010一直遍歷到111...111,第i位為0,高位同k相同
  • 這樣如果修改C[j],同時需要依次修改滿足以下條件的k:
  1. 從右向左尋找j二進位制形式中的0,找到則將0置為1,並將這位的右邊全部置0,可以得到唯一的k,根據上面定義可知D[k]一定包含C[j]
  2. 由於對於j二進位制形式中為1的位置,設此位置為i',根據上面定義無法找到k,滿足f(k)=i'並且D[k]包含C[j]
  • 綜合1、2可知修改時間複雜度也為O(log(n))

上面假設的陣列C下標從0~n-1,而輔助陣列D的下標從0~n,由定義可知在k>0時D[k]一定包含C[k-1],所以可以設Ei+1=Ci,得到新陣列E=(E1,E2,...,En)代替陣列C,此時陣列E與輔助陣列D一一對應

在陣列修改(更新)的過程中,由於i+1等同於將i的最低0位置為1,並將此位右邊全部置0,又由於最低的0位右邊的位對於D[k]的查詢沒有影響,因此將陣列C替換為陣列E,修改E[j]的過程改為:先修改D[j],然後從j二進位制形式最低的非0位開始,從右向左尋找j二進位制形式中的0,找到則將0置為1,並將這位的右邊全部置0,得到k,修改D[k]

 

參考文獻:

https://zh.wikipedia.org/wiki/%E6%A0%91%E7%8A%B6%E6%95%B0%E7%BB%84