先貼一下樹狀陣列的模板**:
int
lowbit
(int i)
void
update
(int i,
int val)
//單點更新
}int
sum(
int i)
//求區間[1,i]內所有元素的和
return ret;
}
模板中最常見的三個函式:①取陣列下標二進位製非0最低位所表示的值;②單點更新;③區間查詢。樹狀陣列,顧名思義是樹狀的陣列,我們首先引入二叉樹,葉子節點代表a[1]~a[8]。
現在變形一下:
現在定義每一列的頂端節點c陣列(其實c陣列就是樹狀陣列),如圖:
c[i]代表子樹的葉子節點的權值之和,如圖可以知道:
c[1]=a[1];
c[2]=a[1]+a[2];
c[3]=a[3];
c[4]=a[1]+a[2]+a[3]+a[4];
c[5]=a[5];
c[6]=a[5]+a[6];
c[7]=a[7];
c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
將c陣列的下標i轉化成二進位制:
1=(001) c[1]=a[1];
2=(010) c[2]=a[1]+a[2];
3=(011) c[3]=a[3];
4=(100) c[4]=a[1]+a[2]+a[3]+a[4];
5=(101) c[5]=a[5];
6=(110) c[6]=a[5]+a[6];
7=(111) c[7]=a[7];
8=(1000) c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
對照式子可以發現:c[i]=a[i-2^ k+1]+a[i-2^k+2]+…+a[i];(k為i的二進位制中從最低位到最高位連續零的個數)
例如:當i=8時,k=3,可以自行代入驗證。現在引入lowbit(x):其實就是取出x的二進位制的最低位1,換言之,lowbit(x)= 2^k,k的含義與上面相同。
int
lowbit
(int i)
/*-i 代表i的負數 計算機中負數使用對應的正數的補碼來表示
例如 : i=6(0110) 此時 k=1
-i=-6=(1001+1)=(1010)
i&(-i)=(0010)=2=2^1
c[i]=a[i-2^k+1]+a[i-2^k+2]+......a[i];
c[i]=a[i-lowbit(i)+1]+a[i-lowbit(i)+2]+......a[i];
*/
接下來是區間查詢(求和):利用c[i]陣列,求a陣列中前i項和:舉兩個栗子:
①i=7,前7項和:sum[7]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7];
而c[4]=a[1]+a[2]+a[3]+a[4];c[6]=a[5]+a[6];c[7]=a[7];
可以得到:sum[7]=c[4]+c[6]+c[7]。
陣列下標寫成二進位制:sum[(111)]=c[(100)]+c[(110)]+c[(111)];
②i=5,前5項和:sum[5]=a[1]+a[2]+a[3]+a[4]+a[5];
而c[4]=a[1]+a[2]+a[3]+a[4];c[5]=a[5];可以得到:sum[5]=c[4]+c[5];
陣列下標寫成二進位制:sum[(101)]=c[(100)]+c[(101)];
細細觀察二進位制,樹狀陣列追其根本就是二進位制的應用,結合**演示一下**過程:
int
sum(
int i)
//求區間[1,i]所有元素的和
return ret;
}
對於i=7進行演示:
7(111) ans+=c[7]
lowbit(7)=001 7-lowbit(7)=6(110) ans+=c[6]
lowbit(6)=010 6-lowbit(6)=4(100) ans+=c[4]
lowbit(4)=100 4-lowbit(4)=0(000) break;
對於i=5進行演示:5(101) ans+=c[5]
lowbit(5)=001 5-lowbit(5)=4(100) ans+=c[4]
lowbit(4)=100 4-lowbit(4)=0(000) break;
最後是單點更新:當我們修改a陣列中某個值時,應當如何更新c陣列呢?回想一下,區間查詢的過程,再看一下上文中列出的過程。這裡宣告一下:單點更新實際上是不修改a陣列的,而是修改樹狀陣列c,向上更新區間長度為lowbit(i)所代表的節點的值。void
update
(int i,
int val)
//更新單節點的值
}//可以發現 更新過程是查詢過程的逆過程
//由葉子結點向上更新c陣列
如圖:當在a[1]加上值val,即更新a[1]時,需要向上更新c[1],c[2],c[4],c[8],這個時候只需將這4個節點每個節點的值加上val即可。這裡為了方便大家理解,人為新增了個a陣列表示每個葉子節點的值,事實上a陣列並不用修改,實際運用中也可不設定a陣列,單點更新只需修改樹狀陣列c即可。下標寫成二進位制:c[(001)],c[(010)],c[(100)],c[(1000)];
lowbit(1)=001 1+lowbit(1)=2(010) c[2]+=val;
lowbit(2)=010 2+lowbit(2)=4(100) c[4]+=val;
lowbit(4)=100 4+lowbit(4)=8(1000) c[8]+=val;
最後說一下樹狀陣列的優缺點:①特點:**短小,實現簡單;容易擴充套件到高緯度的資料;②缺點:只能用於求和,不能求最大/小值;不能動態插入;資料多時,空間壓力大。
掌握樹狀陣列 徹底入門
大佬部落格 先貼一下樹狀陣列的模板 int lowbit int i void update int i,int val 單點更新 int sum int i 求區間 1,i 內所有元素的和 return ret 模板中最常見的三個函式 取陣列下標二進位製非0最低位所表示的值 單點更新 區間查詢。樹...
樹狀陣列徹底入門
int lowbit int t void add int x,int y int getsum int x 這篇筆記 會詳細的講解,使得隊員們對樹狀陣列徹底入門 而不是懵懵懂懂。以上先給出 最常見的,三個函式。單點更新,區間查詢 網上的解釋以及分析有很多,這裡是我的一點總結和體會歸納一下,並且在週...
樹狀陣列1 樹狀陣列入門
仔細看一下,發現tree的每乙個節點的高度並不是隨意的,而是由它轉成二進位制之後末尾連續零的數量決定的,連續零的數量加1,就是高度,例如 3 11 零的數量為0,加1等於1,所以它的高度就是1 6 110 零的數量為1,加1等於2,所以它的高度就是2 8 1000 零的數量為3,加1等於4,所以它的...