樹狀陣列基礎知識

2021-07-27 20:05:38 字數 2231 閱讀 4920

已知陣列a,元素個數為n,現在要求a陣列中i到j區間內的和(1<=i<=j<=n).

我們完全可以儲存sum[1,k](k=1,2,……),然後對任意給定的查詢區間[i,j],都可以方便的用ans=sum[1,j]-sum[1,i-1],當然這只是沒有元素改變的情況下的比較優化的解法.那麼對於對於陣列中的元素隨時變更的情況下,我們能否還這麼做呢?

如果仍然採取這樣的方法,則每次資料有更新,則需要將更新的元素後的sum值全部再求一次。假設有m次查詢或者更新操作,則時間複雜度將達到m*n了。有沒有更好的辦法來縮小這個時間複雜度嗎?

可以想一下,每次更改的元素可能是比較少的,有時候甚至每次只改變乙個元素,但是在用暴力方法求區間和的時候,卻對區間內所有的元素都累加了一遍,這樣其實造成了許多無謂的運算.這時候也許會想到如果能把一些結果存起來會不會減少很多運算?答案是肯定的,但問題是怎麼存,存什麼?

我們可以利用二進位制的思想。

令這棵樹的結點編號為c1,c2……cn。令每個結點的值為這棵樹的值的總和,那麼容易發現:

c1 = a1

c2 = a1 + a2

c3 = a3

c4 = a1 + a2 + a3 + a4

c5 = a5

c6 = a5 + a6

c7 = a7

c8 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8

…… c16 = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + a11 + a12 + a13 + a14 + a15 + a16

這裡a是原陣列,c是樹狀陣列。

這裡有1個有趣的性質:設節點編號為x,那麼這個節點管轄的區間為2^k(剛好為x的二進位制中最後乙個1表示的值)。因為這個區間最後乙個元素必然為ax,所以很明顯: cx

=a(x

–2k+

1)+…

…+ax

算這個2^k有乙個快捷的辦法,定義乙個函式如下即可:

int lowbit(int x)
當然,大家仔細回想一下以前學過的位運算,則lowbit還有這樣的寫法:

int lowbit(int x)

也可以這樣寫:

#define lowbit(x) (x&-x)
當想要查詢乙個sum(n)時,可以依據如下演算法即可:

step1: 令sum = 0,轉第二步;

step2: 假如n <= 0,演算法結束,返回sum值,否則sum = sum + cn,轉第三步;

step3: 令n = n – lowbit(n),轉第二步。

可以看出,這個演算法就是將這乙個個區間的和全部加起來,為什麼是效率是log(n)的呢?以下給出證明:

n = n–lowbit(n)這一步實際上等價於將n的二進位制的最後乙個1減去。而n的二進位制裡最多有log(n)個1,所以查詢效率是log(n)的。

如果修改乙個節點,必須修改其所有祖先,最壞情況下為修改第乙個元素,最多有log(n)的祖先。所以修改演算法如下(給某個結點i加上x):

step1: 當i > n時,演算法結束,否則轉第二步;

step2: ci = ci + x, i = i + lowbit(i)轉第一步。

下面是樹狀陣列的基本函式:

const

int max = 50000;

#define lowbit(x) (x&-x)

int tree[max+1],n,t;

//更新元素

void update(int pos,int val)

}//求和

int getsum(int x)

return sum;

}//初始化

int main()

}

樹狀陣列比較適合用來求動態陣列的區間之和.其最基本的函式是lowbit(x)。

一次更新的時間複雜度是log(n)

一次求和是log(n).

對於最開始提出的問題,即n個元素的動態陣列進行m次查詢或更新,其總的時間複雜度為mlog(n).

樹狀陣列的實現是非常簡單,思想很巧妙,有一定的應用範圍。大家一定要好好學會。

下面讓我們來看幾道例題。

陣列 基礎知識

陣列是相同型別的變數的有序集合 int a 5 陣列示意圖 陣列包含5個int型別的資料 陣列在一片連續的記憶體空間中儲存元素。陣列元素的個數可以顯式或隱式指定。對於a,a 2 a 3 a 4 都為0 對於b,元素個數為2。在定義陣列並初始化的時候,給陣列的前幾位初始化設定相應的值之後,如果沒有給後...

陣列基礎知識

陣列 1 定義 儲存相同型別的一組資料 格式 陣列型別 陣列名 new 陣列型別 陣列長度 基本要素 識別符號 陣列元素 元素下標 從0開始 元素型別 初始化陣列 方式 1 int nums new int 3 方式 2 int nums 方式 3 int nums new int nums 0 1...

陣列基礎知識

陣列的宣告 陣列是相同型別的資料按順序組成的一種復合資料型別。通過陣列名加陣列下標,來使用陣列中的資料。下標從 0 開始排序。宣告一維陣列 陣列元素型別 陣列名 宣告二維陣列 陣列元素型別 陣列名 陣列的例項化 陣列的初始化 靜態初始化 格式一 資料型別 陣列名 格式二 資料型別 陣列名 new i...