線段樹的建立 查詢 更新及應用

2021-08-09 08:45:07 字數 2852 閱讀 4473

線段樹本質上是一棵二叉搜尋樹(體現在查詢階段),與普通的二叉樹不一樣,這個二叉樹有點特別:用left和right來表示乙個區間,即 [left, right],而其值val,則用來表徵這個區間的某些特徵,例如:區間最大值,區間最小值,區間和;並且容易知道,當left和right相等時,代表葉結點,其值就是某個陣列當前位置的值。

一棵區間為[1, 5]上的線段樹

線段樹在處理某些問題時是非常方便的,乙個經典的問題是:給你乙個陣列,總共n個值(陣列從1到n排列),然後給你乙個區間 [l, r],對於每次查詢,給出區間 [l, r]的和(或者最大值,最小值)

聰明的小夥伴一定會馬上想到用字首和來處理這個問題,是的,字首和在預處理階段為o(n)複雜度,而每次查詢只需要o(1),對於這種靜態問題是十分方便的,但是,如果我們在上面的問題中加上 「每次隨機更新陣列中的某個值」 這一條件時,維護字首和的開銷其實是十分巨大的,因為對於每次修改,你不得不對字首和進行o(n)的更新,如果資料量過大,這種方式是肯定不行的。所以,我們採用線段樹這一資料結構來解決這個問題

典型的線段樹可以採用如下定義:

struct treenode

tree[4*n];

以下為線段樹的三個操作,建樹,查詢,更新

注:**以求區間和為例

void build(int root,int l,int r)//建樹

int mid=(l+r)/2;

build(2*root,l,mid);//左子樹

build(2*root+1,mid+1,r);//右子樹

tree[root].val=tree[2*root].val+tree[2*root+1].val;//求和

}

int query(int root,int l,int r)//查詢[l,r]的和

int mid=(tree[root].left+tree[root].right)/2;

if(mid>=r)

return query(2*root,l,r);//在左子樹查詢

else

if(mid+1

<=l)

return query(2*root+1,l,r);//在右子樹查詢

return query(2*root,l,mid)+query(2*root+1,mid+1,r);//跨區間查詢

}

void update(int root,int pos,int val)//更新pos位置的值為val

int mid=(tree[root].left+tree[root].right)/2;

if(pos<=mid)

update(2*root,pos,val);

else update(2*root+1,pos,val);

tree[root].val=tree[2*root].val+tree[2*root+1].val;//更新和

}

線段樹每次查詢和更新操作時,其複雜度為o(logn),這對於我們來說,是較為理想的。

應用問題:

第一行給出n,m,分別為陣列元素的個數以及請求個數

第二行給出n個數,

隨後m行,每行給出乙個操作:opt x y

當opt = 1,更新陣列中x位置的值為y

當opt = 2,輸出[x, y]的區間和

注:下標從1開始

/* test case

8 41 2 3 4 5 6 7 8

1 2 0

2 1 8

1 8 0

2 2 2

*//*output340

*/

#include 

#define n 1024

using

namespace

std;

struct treenode

tree[4*n];

int arr[n];

void build(int root,int l,int r)

int mid=(l+r)/2;

build(2*root,l,mid);

build(2*root+1,mid+1,r);

tree[root].val=tree[2*root].val+tree[2*root+1].val;

}int query(int root,int l,int r)

int mid=(tree[root].left+tree[root].right)/2;

if(mid>=r)

return query(2*root,l,r);

else

if(mid+1

<=l)

return query(2*root+1,l,r);

return query(2*root,l,mid)+query(2*root+1,mid+1,r);

}void update(int root,int pos,int val)

int mid=(tree[root].left+tree[root].right)/2;

if(pos<=mid)

update(2*root,pos,val);

else update(2*root+1,pos,val);

tree[root].val=tree[2*root].val+tree[2*root+1].val;

}int main()

HDU1754 《線段樹建立,查詢,更新》

the reason of failure 1 又一次被cin的速度坑了!注意大資料要用scanf的輸入方法啊。learning 1 線段樹是用來幹嘛的,是可以找區間最大最小值或者和之類的值的。thinking 每次這個線段的max1取max max1,weight 也就是判斷改變的這個值,是否大於...

線段樹的構建,查詢與更新

線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。使用線段樹可以快速的查詢某乙個節點在若干條線段 現的次數,時間複雜度為o logn 而未優化的空間複雜度為2n,實際應用時一般還要開4n的陣列以免越界,因此有時需要離散化讓空間壓縮。想要理解線...

線段樹的建樹 更新 查詢操作

模板題sdut oj 3771 陣列計算機 description blue 有乙個神器的機器,這個機器可以讀入乙個陣列,並按照使用者要求快速地進行陣列的處理和計算,它支援如下兩種操作 操作 1 把陣列中第 p 個元素的值增加 v。操作 2 計算陣列中 l,r 區間內所有數的和。這個機器就是這麼的神...