lowbit (i):i&-i
或者i=i&(i^i-1)
9 and -1
1
、要點:
乙個數i對應二進位制表示未尾0的個數為k,那麼他管轄的範圍為:從i到左邊i-2(k)+1的2(k)個元素。如:i:6 110 k=1 6管轄的範圍為:
c[5]=a[5]+a[6]; 2(1)
管2個。
2、求前n個元素的和,n的二進位制的
最後乙個1減
去。如:n=7;111
à110(6)->100(4)->0
i=i-lowbit(i)
c[7]=a[4]+a[6]+a[7]
c[8]= 1
項a[8] 1000->0
c[i]=c[i]+c[i-2(k)]
k
表示i當前未尾0的個數
-
àc=7 k=0; c=7-2(0)=7-1=6
c[7]=c[7]
--
》k表示未尾0的個數
c=6 k=1 c=6-2(1)=4
c[7]=c[7]+c[6]
--
》c=4,k表示未尾0的個數
c=4.k=2; c=4-2(2)=0
c[7]=c[7]+c[6]+c[4]
c=13
à1101->
第一種思想:
c[13]=1101c[1100=12]=c[1000=8]=c[0]
c[13]=c[13]+c[12]+c[8]
3
、如果要修改a[i],應該要修改:
i=i+2(k)
c[i].
一直到根結點。3 11 ->4->8->16
如:i=15 a[15] 1111 c[i]=15+2(0)=15+1=16
4
、動態求和:如果要求a陣列中某段元素的和,則只需要統計c陣列即可。
《1》sum=sum+c[i]
《2》i=i-lowbit(i)
重複直到i等於0
i=7
要求a[1]~a[7]的和
sum=0+c[7] i=7-2(0)=6 sum=c[7]+c[6]+c[4] i=6-2(1)=4 4=4-2(2)=0
a[2]
到a[7]可以先求
a[1]
到a[7]-c[2]
樹 狀 數 組
1
、i=i+lowbit(i)向上走,用於更新a陣列->c[i]
//
解釋i=i+lowbit(i)表示把i未尾1補0的過程。
2
、i=i-lowbit(i)用於求a[1]到a[i]的和,可以通過求c[i]的和來得.
//
解釋i=i-lowbit(i)表示把i的最後乙個1減去。
1
、概述
樹狀陣列(binary indexed tree),是一種設計新穎的陣列結構,它能夠高效地獲取陣列中連續n個數的和。概括說,樹狀陣列通常用於解決以下問題:陣列中的元素可能不斷地被修改,怎樣才能快速地獲取連續幾個數的和?
2
、樹狀陣列基本操作
傳統陣列(共n個元素)的元素修改和連續元素求和的複雜度分別為o(1)和o(n)。樹狀陣列通過將線性結構轉換成偽樹狀結構(線性結構只能逐個掃瞄元素,而樹狀結構可以實現跳躍式掃瞄),使得修改和求和複雜度均為o(lgn),大大提高了整體效率。
給定序列(數列)a,我們設乙個陣列c滿足
c[i] = a[i–2^k+ 1] + … + a[i]
其中,k為i在二進位制下末尾0的個數,i從1開始算!
則我們稱c為樹狀陣列。
下面的問題是,給定i,如何求2^k?
答案很簡單:2^k=i&(i^(i-1)) ,也就是i&(-i)
下面進行解釋:
以i=6為例(注意:a_x表示數字a是x進製表示形式):
(i)_10 = (0110)_2
(i-1)_10=(0101)_2
i xor (i-1) =(0011)_2
i and (i xor (i-1)) =(0010)_2
2^k = 2
c[6] = c[6-2+1]+…+a[6]=a[5]+a[6]
陣列c的具體含義如下圖所示:
當我們修改a[i]的值時,可以從c[i]往根節點一路上溯,調整這條路上的所有c即可,
i=i-2(i)
i=i+2(i)
表示把未尾1補0的過程如:i=4;
a[4]
à10000 c[i] i=i+lowbit(i)(2k)
àlowbit(i)=i&i^(i-1)=i&(-i)=2(k)
a[5]
ài=5 101 i=5+1=6 110 ->i=6+2=8 1000->i=
i=13
加2(0)=14 +2(1)=16
a[7]
被修改:i=7 111 i=i+2(0)=7+1=8 1000 i=i+2(3)=16
這個操作的複雜度在最壞情況下就是樹的高度即o(logn)。另外,對於求數列的前n項和,只需找到n以前的所有最大子樹,把其根節點的c加起來即可。不難發現,這些子樹的數目是n在二進位制時1的個數,或者說是把n展開成2的冪方和時的項數,因此,求和操作的複雜度也是o(logn)。
樹狀陣列能快速求任意區間的和:a[i] + a[i+1] + … + a[j],設sum(k) = a[1]+a[2]+…+a[k],則a[i] + a[i+1] + … + a[j] = sum(j)-sum(i-1)。
下面給出樹狀陣列的c語言實現: 1
2345
6789
1011
1213
1415
1617
1819
2021
//求2^k
int lowbit(int t)
//求前n項和
int sum(int end)
return sum;
//增加某個元素的大小
void plus(int pos, int num)
}3
、擴充套件——二維樹狀陣列
一維樹狀陣列很容易擴充套件到二維,二維樹狀陣列如下所示:
c[x][y] = sum(a[i][j])
其中,x-lowbit[x]+1 <= i<=x且y-lowbit[y]+1 <= j <=y
(2)二維樹狀陣列:
乙個由數字構成的大矩陣,能進行兩種操作
1) 對矩陣裡的某個數加上乙個整數(可正可負)
2) 查詢某個子矩陣裡所有數字的和
要求對每次查詢,輸出結果
5
、總結
樹狀陣列最初是在設計壓縮演算法時發現的(見參考資料1),現在也會經常用語維護子串行和。它與線段樹(具體見:資料結構之線段樹
)比較在思想上類似,比線段樹節省空間且程式設計複雜度低,但使用範圍比線段樹小(如查詢每個區間最小值問題)。
c++源程式l
#include
#include
using namespace std;
ifstream in("樹狀陣列.in");
ofstream out("樹狀陣列.out");
#define max 16
int n,a[max],c[max],m;
int lowbit(int i)
void genxin(int i,int x)
}int getsum(int i)
return sum;
}void init() }
void print()
{cout<<"原陣列為:";
for(int i=1;i<=n;i++)
cout<>m;
cout<
樹狀陣列高階
csdn同步 前置知識 下面我們考慮區間修改。一開始我們維護的是部分字首和,但是現在,區間修改顯然不能用字首和有關的做法。單區間修改用差分就夠了。但是這裡有修改,考慮將差分和樹狀陣列結合。令 c 為 a 的差分,維護 a 的區間修改與區間和即可,這樣 a 就是 c 的字首和了。那麼區間修改只需要改 ...
樹狀陣列專題總結1
如果給定乙個陣列,要你求裡面所有數的和,一般都會想到累加。但是當那個陣列很大的時候,累加就顯得太耗時了,時間複雜度為o n 並且採用累加的方法還有乙個侷限,那就是,當修改掉陣列中的元素後,仍然要你求陣列中某段元素的和,就顯得麻煩了。所以我們就要用到樹狀陣列,他的時間複雜度為o lgn 相比之下就快得...
高階版樹狀陣列
我們都知道樹狀陣列一般有兩種形式 1.最為傳統的版本,支援區間求和,單點修改 2.差分樹狀陣列 支援區間修改,單點查詢 而高階版樹狀陣列 可支援 區間求和,區間修改 其原理是 設tree i a i a i 1 差分 那麼容易得到 tree 1 tree 2 tree i a i 這個公式 維護tr...