樹狀陣列詳解

2021-09-12 07:11:12 字數 2861 閱讀 9665

樹狀陣列(binary indexed tree(b.i.t), fenwick tree)是乙個查詢和修改複雜度都為log(n)的資料結構。

實際上樹狀陣列就是沒有右兒子的線段樹,它實現起來比線段樹簡單,而且效率更高,空間占用的也更少。但是能用樹狀陣列解決的問題,基本上都能用線段樹解決。(反過來就不是了)

不多解釋了,先上幾張圖:

我們用陣列a表示原序列:a1、a2……an

用陣列c代表樹狀陣列,從上圖中我們可以發現:

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

對應陣列c下標的二進位制表示:

c1的下標1的二進位制表示 1 末尾有0個連續的0 該節點管理1個元素

c2的下標2的二進位制表示 10 末尾有1個連續的0 該節點管理2個元素

c3的下標3的二進位制表示 11 末尾有0個連續的0 該節點管理1個元素

c4的下標4的二進位制表示 100 末尾有2個連續的0 該節點管理4個元素

由此我們得到乙個規律:我們設i的二進位制表示的末尾有k個連續的0,那麼ci管理2^k個元素。那麼有什麼辦法可以很快的算出來2^k呢?我們有乙個名為lowbit的神奇操作:x&(-x)就是我們想要的答案。且對於節點i,i+lowbit(i)就是節點i的父節點

我們可以對照上圖,若我們求[1,7]的區間和,那麼sum=c7+c6+c4,即三次操作就得到了我們想要的結果,這也是為什麼查詢的複雜度是o(lgn)的,那麼我們看看lowbit發揮了怎樣的作用吧:7-lowbit(7)=6     6-lowbit(6)=4    4-lowbit(4)=0。可以發現,我們求和的操作就是從區間右端點r開始,不斷地累加cr,同時對r作lowbit操作。那麼修改操作呢,就逆著來,即從要開始的子節點i開始,不斷修改ci,同時對i做lowbit操作。有人可能會問,你這求的是[1,r]的和啊,我要求[l,r]的和怎麼辦呢?很簡單,把[1,r]和[1,l-1]的和均求出來,然後用前者減去後者即可得到答案。

這裡我以求某段區間和作為例題來介紹一下線段樹的基本操作。(注意 樹狀陣列的下標是從1開始的)

lowbit操作:

inline int lowbit(int x)

更新操作:(單點修改)

void update(int i,int v)

查詢操作:

int sum(int l)

一位樹狀陣列的內容就到這裡了~大家可以做題體會一下~

舉乙個簡單的例子,我們有16個元素:a1、a2、a3、a4、……a16構成了乙個4*4的矩陣,我想查詢某個矩陣內所有元素的和,並且可能會修改該矩陣的某個元素怎麼做呢?樸素演算法肯定是遍歷統計和,複雜度o(n^2),那有沒有o((lgn)^2)的演算法呢?肯定有,這就是我們要介紹的二維陣列,首先要知道對於tree[i][j]的每乙個tree[i]肯定是乙個一維的樹狀陣列,但是其儲存的東西可能跟你想的有點區別:(並不是簡簡單單的tree[1]管理a1-a4   tree[2]管理a5-a8哦)

我們先寫出四個一維的樹狀陣列:

tree1[1]=a1    tree1[2]=a1+a2     tree1[3]=a3     tree1[4]=a1+a2+a3+a4

tree2[1]=a5    tree2[2]=a5+a6     tree2[3]=a7     tree2[4]=a5+a6+a7+a8

tree3[1]=a9    tree3[2]=a9+a10    tree3[3]=a11     tree3[4]=a9+a10+a11+a12

然後再寫出我們的二維樹狀陣列:

tree[1][1]=a1      tree[1][2]=a1+a2       tree[1][3]=a3       tree[1][4]=a1+a2+a3+a4

tree[2][1]=a1+a5       tree[2][2]=a1+a2+a5+a6        tree[2][3]=a3+a7      tree[2][4]=a1+a2+a3+a4+a5+a6+a7+a8

tree[3][1]=a9      tree[3][2]=a9+10        tree[3][3]=a11     tree[3][4]=a9+a10+a11+a12

也就是說tree[i]維護的是:treei+tree(i-1)+……+tree(i-lowbit(i)+1) 比如上面tree[2]維護的就是tree2和tree1

那麼我們求這個矩陣從第1行到第x行,第1列到第y列的操作就很簡單了:(就是原來的一維變成了二維)

至於怎麼求中間的某個矩陣的和就留給大家思考了~

void add(int x,int y,int v)

int query(int x,int y)

樹狀陣列處理rmq問題:

修改:

void updata(int x)

return ans;

}

針對區間[1,r]的查詢:

int query(int r)//查詢[1,r]

return ans;

}

樹狀陣列 詳解

對於普通陣列,其修改的時間複雜度位o 1 而求陣列中某一段的數值和的時間複雜度為o n 因此對於n的值過大的情況,普通陣列的時間複雜度我們是接受不了的。在此,我們引入了樹狀陣列的資料結構,它能在o logn 內對陣列的值進行修改和查詢某一段數值的和。假設a陣列為儲存原來的值得陣列,c為樹狀陣列。我們...

樹狀陣列詳解

樹狀陣列求區間和的一些常見模型 樹狀陣列在區間求和問題上有大用,其三種複雜度都比線段樹要低很多 有關區間求和的問題主要有以下三個模型 以下設a 1.n 為乙個長為n的序列,初始值為全0 1 改點求段 型,即對於序列a有以下操作 修改操作 將a x 的值加上c 求和操作 求此時a l.r 的和。這是最...

樹狀陣列詳解

比如說,我這裡有一組數1,2,3,2,k。我想知道第i到第j的和 mathop sum limits j v i 是多少?樸素演算法 for int k 0 k n k if k i k j ans v k 類似這種的寫法,雖然在某些點值改變時也依然可以計算 我們稱這種問題為動態問題 但複雜度最高到...