樹狀陣列基礎

2021-10-03 06:23:10 字數 3080 閱讀 3862

樹狀陣列,又稱二進位制索引樹,英文名binary indexed tree。

樹狀陣列用來求區間元素和,求一次區間元素和的時間效率為o(logn)。

有些同學會覺得很奇怪。

用乙個陣列s[i]儲存序列a的前i個元素和,那麼求區間i,j的元素和不就為s[j]-s[i-1],那麼時間效率為o(1),豈不是更快?

但是,如果題目的a會改變呢?

例如:我們來定義下列問題:我們有n個盒子。

可能的操作為:

1.查詢從盒子i到盒子j總的石塊數

2.向盒子k新增石塊

操作1為o(1)的複雜度,s[j]-s[i-1即可。

而對操作2為o(n)的時間複雜度,需要更新s[k]到s[n]。

但是用樹狀陣列,對操作1和2的時間複雜度都為o(logn)。

先上圖

若區間的結尾為r,則區間長度就等於r的二進位制分解下的最小二次冪,即lowbit(r)

例如x=7=22+21+20

lowbit(7)=1,也就是c[7]區間有乙個數a[7]

右區間是7,區間長度是lowbit(7),那麼左區間就是r-lowbit(r)+1

//lowbit:從右往左第乙個1的位置

intlowbit

(int x)

下面的**可以計算出區間[1,x]分成幾個小區間

while

(x>0)

對於給定的序列a,我們建立乙個陣列c,其中c[x]儲存序列a的區間[x-lowbit(x)+1,x]中所有數的和。

例如a[1]~a[8]

c[8] 右區間為8, 8-lowbit(8)+1=1是左區間,也就是c[8]代表

a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]這8個數的和。

如果求前8個元素的字首和,那麼c[8]就是

樹狀陣列求字首和的**

int

ask(

int x)

//為右區間,左區間預設為1

return ans;

}

int ask(int x)函式就是求x的字首和,當x=8,ans+=c[8]

執行x=x-lowbit(8)x=0,迴圈退出。c[8]這個區間長度為8(區間長度由lowbit(8)求出),正好是a[1]~a[8]的和。

ask(7)表示求前7個陣列元素的和

ans=ans+c[7];

執行x=7-lowbit(7) x=6

ans=ans+c[6] ;

執行x=6-lowbit(6) x=4

ans=ans+c[4];

執行x=4-lowbit(4) x=0

迴圈退出。

也就是前7個元素的字首和就是c[7]+c[6]+c[4]這幾個小區間的和

c[7]的區間長度為lowbit(7)=1, 右區間為7,左區間為7-lowbit(7)+1=7,也就是c[7]的區間是[7,7]只包含乙個a[7]

c[6]的區間長度為lowbit(6)=2 ,右區間為6,左區間為6-lowbit(6)+1=5,也就是c[6]的區間為[5,6],它有a[5]、a[6]的和。

c[4]的區間長度為lowbit(4)=4, 右區間為4,左區間為4-lowbit(4)+1=1, 也就是c[4]的區間為[1,4],它有a[1]、a[2]、a[3]、a[4]的和。

通過上面的分析,我們知道ask(7)有c[7]、c[6]、c[4]三個小區間的和組成。

c[7]=a[7]

c[6]=a[6]+a[5]

c[4]=c[4]+c[3]+c[2]+c[1]

我們已經知道樹狀陣列c是這樣構成的,我們怎麼初始化乙個樹狀陣列

如果使用下面的**

//樹狀陣列初始化

#include

#include

#include

using

namespace std;

const

int n=8;

int a[n+1]

=;int c[n+1]

;int

lowbit

(int x)

intask

(int x)

return ans;

}int

main()

}for

(int x=

1;x<=n;x++

)printf

("%d "

,c[x]);

printf

("\n");

printf

("%d\n"

,ask(4

));return0;

}

上面的**在初始化樹狀陣列c[ ]時,用到了循化的巢狀o(n2),這是不可取得,我們使用樹狀陣列的目的就是為時間複雜度位o(log n)

用o(log n)的方法初始化樹狀陣列

//用樹狀陣列單點增加來初始化

#include

#include

#include

using

namespace std;

const

int n=8;

int a[n+1]

=;int c[n+1]

;int

lowbit

(int x)

void

add(

int x,

int y)

//單點增加

intask

(int x)

//求字首和

intmain()

for(

int x=

1;x<=n;x++

)printf

("%d "

,c[x]);

printf

("\n");

printf

("%d\n"

,ask(4

));return0;

}

基礎樹狀陣列

la 4329 2008年北京區域賽 題目分析 首先是乘法原理加法原理計數,對a i 當做裁判的情況,記c i d i 分別表示前面和後面小於它的數的個數,ai當裁判方法數就是 c i n i 1 d i d i i c i 了 具體的見 演算法競賽入門經典訓練指南 197頁 樹狀陣列見194 19...

樹狀陣列基礎

基本用法 例題與線段樹比較 樹狀陣列的基本操作流程與線段樹很像,故學習起來時是兩者的操作對比起來學的。線段樹學習之單點更新與區間更新 樹狀陣列 binary indexed tree 是一種與線段樹類似的資料結構,又叫二進位制索引樹 spoil alert 跟二進位制有關 主要用於查詢區間和問題,每...

樹狀陣列基礎

關於樹狀陣列的講解推薦 演算法競賽入門經典訓練指南 一維版本 洛谷3374 分析 樹狀陣列裸的模板題 1 include2 include3 include4 using namespace std 5const int maxn 500000 10 6 intc maxn 7int n,m 8in...