資料結構之線段樹

2021-08-01 03:45:49 字數 2605 閱讀 8521

一、引例

有m個數排成一列,做n次操作,每次操作包括:

(1)詢問指定區間的最大值、最小值

(2)將指定區間的每個數加上乙個值

如果按照最樸素的做法,乙個個的遍歷,時間複雜度:o(mn)。

那麼如何解決乙個區間求和(最大值,最小值)的問題呢?那麼就要用到線段樹啦。

二、定義

線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。

主要用來解決區間查詢、區間修改,使用線段樹可以快速的查詢某乙個節點在若干條線段中出現的次數,基本保證每次操作的時間複雜度為o(logn)。

三、實際應用

a.單點更新

struct node

tree[maxn<<2];

2.更新

void maintain(int root)//更新根節點為左右子結點的和

3.遞迴建樹

遇到葉子節點直接賦值,否則遞迴遍歷左右建樹,最後回溯即可。

void build(int root,int begin,int end)//樹的結點編號 左端點下標 右端點下標

int mid=(begin+end)>>1;

build(root<<1,begin,mid);//更新左子樹

build(root<<1+1,mid+1,end);//更新右子樹

maintain(root);//儲存左右子樹的和

}

4.單點更新

將一條線段[a,b] 插入到代表線段[l,r]的結點p中,如果p不是元線段,那麼令mid=(l+r)/2。如果b<mid,那麼將線段[a,b] 也插入到p的左兒子結點中,如果a>mid,那麼將線段[a,b] 也插入到p的右兒子結點中。

void update(int root,int pos,int num)

//根結點編號 欲修改值的下標 期待的值

int mid=(tree[root].left+tree[root].right)>>1;

if(pos<=mid)

update(root<<1,pos,num);

else

update(root<<1+1,pos,num);

maintain(root);//每次都要更新根節點

}

5.求和操作

int query(int root,int begin,int end)//求和

int mid=(tree[root].left+tree[root].right)>>1;

if(end

<=mid)

ans+=query(root<<1,begin,end);

else

if(begin>=mid+1)

ans+=query(root<<1+1,begin,end);

else

return ans;

}

b.區間更新(成段更新)

比如 從[1,10]每個結點的值都+1,普通單點更新就會超時。

*區間更新:

指更新某個區間內的葉子節點的值,因為涉及到的葉子節點不止乙個,而葉子節點會影響其相應的非葉父節點,那麼回溯需要更新的非葉子節點也會有很多,如果一次性更新完,操作的時間複雜度肯定不是o(lgn),例如當我們要更新區間[0,3]內的葉子節點時,需要更新出了葉子節點3,9外的所有其他節點。為此引入了線段樹中的延遲標記概念,這也是線段樹的精華所在。

*延遲標記:

因為更新的數很多,所以我每一步的更新不接著算出來,等到最後需要的時候再去取消標記算出來。

比如現在需要對[a,b]區間值進行加c操作,那麼就從根節點[1,n]開始呼叫update函式進行操作,如果剛好執行到乙個子節點,它的節點標記為rt,這時tree[rt].l == a && tree[rt].r == b 這時我們可以一步更新此時rt節點的sum[rt]的值,sum[rt] += c * (tree[rt].r - tree[rt].l + 1),注意關鍵的時刻來了,如果此時按照常規的線段樹的update操作,這時候還應該更新rt子節點的sum值,而lazy思想恰恰是暫時不更新rt子節點的sum值,到此就return,直到下次需要用到rt子節點的值的時候才去更新,這樣避免許多可能無用的操作,從而節省時間 。

用lazy標記,等到當前區間比我需要的目標區間大的時候,我必須用到下面的值了,必須往下修改了,這時候,我們就把之前堆積起來的懶惰標記pushdown了,於是就有了乙個神奇的pushdown操作。

其他的建樹什麼的和單點更新一樣,只是多了lazy標記和pushdown。

void pushdown(ll root)  //向下傳遞lazy標記 

}

c.區間合併

不過還沒做過這方面的題qaq

參考部落格

很有趣很好懂

很官方很全面

來做題吧 超全

ps:之前看過,今天再看像重新學了一遍(>_<)

今天距離省賽過去已經乙個星期了,該調整回來了。

還是不夠強,繼續修煉吧qaq。

資料結構之線段樹

線段樹也叫區間樹,顧名思義,線段樹是一種基於區間的樹,每個節點表示乙個 線段 或 區間 樹的根節點表示是 整體 的區間,左右子樹分別表示這個區間的左半邊和右半邊。function 以節點v為根建樹 v對應區間為 l,r 線段樹的關鍵在於如何定義樹節點,以及如果構建 插入 樹節點。1.樹節點的定義 p...

資料結構之線段樹

線段樹是一種二叉查詢樹,它將乙個區間劃分為1個個單元,樹的每個節點都是1個單元。如下圖的樹就是一顆區間樹。性質 對於線段樹中的每乙個非葉子節點 a,b 它的左節點為 a,a b 2 右節點為 a b 2 1,b 線段數是平衡二叉樹,子節點的個數等於整個區間的長度。建樹 在這裡,我們使用陣列來實現簡單...

關於資料結構之線段樹

這幾天都一直在看關於線段樹的題目還有題解,還有做題!以前也知道有線段樹這個東西,但是那時沒有好好的看,就看了個簡單的皮毛!所以現在又拿出來好好看看!一開始看,一直看題解,還有就是模仿,首先看一遍,初步了解一下,然後就是按著別人的題解再抄著寫一遍直到通過測試,當然不是直接對著乙個乙個的copy上,然後...