樹狀陣列 演算法高階專題

2021-06-20 11:21:50 字數 4935 閱讀 7069

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...