已知陣列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...