有一類區間問題可以抽象成如下模型。 給定包含 n個數的陣列 a1,a2,⋯an。有兩種操作
查詢區間 [l,r] 最小的數。
修改第 ai為 x。
這裡,為了解決這個問題,我們介紹一種靈活的資料結構——線段樹。
我們用一棵二叉樹來表示線段樹,線段樹中的每個結點都表示乙個區間。每個非葉子結點都有左右兩棵子樹,分別對應區間的 「左半」 和 「右半」。為了方便起見,我們給根結點編號為 1。對於每個結點,其左結點的編號為 2i,其右結點的編號為 2i+1。
對於乙個結點,如果其表示的區間為 [l,r]。分情況如果 l=r,那麼這個是乙個葉子結點。否則令 mid=⌊,左兒子對應的區間為 [l,mid],右兒子對應的區間為 [mid+1,r],這一思想有點類似二分。下面就是 n=10 的時候的線段樹。
假定根結點表示長度為 2h 的區間,不難發現,樹的第 i 層有 2i 個結點,每個結點對應乙個長度為 2h-i 的區間。最大層的編號為 h,結點總數為 1+2+4+8+⋯+2h = 2h+1 - 1,略小於區間長度的兩倍。而當整個區間長度不是 2 的整數冪時,雖然葉子結點不在同一層,但樹的最大層編號和結點總數仍滿足上述結論。
前面構建的線段樹,只是展示了線段樹中各結點所對應的區間,但是對於用到線段樹的大部分題目來說,這些線段所擁有的附加資訊才是重頭戲。比如要維護區間最小值問題,我們用乙個額外的陣列minv記錄每個結點對應的區間的最小值。 對於葉子結點,最小值就是乙個數。而對於非葉子結點,區間的最小值就是左兒子的最小值和右兒子最小值中的最小值。
可以發現這個構建過程是乙個遞迴的過程,父節點的資訊需要用子節點去更新,所以我們需要先遞迴的構建好左右子樹。見下面**。
const
int maxn =
10010
;int minv[
4* maxn]
, a[maxn]
;// id 表示結點編號,l, r 表示左右區間
void
build
(int id,
int l,
int r)
int mid =
(l + r)
>>1;
build
(id <<
1, l, mid)
;build
(id <<1|
1, mid +
1, r)
; minv[id]
=min
(minv[id <<1]
, minv[id <<1|
1]);
}
有乙個需要特別注意的地方地方,雖然線段樹中總的結點數是區間長度的兩倍,但是實際上,我們結點的編號不一定是連續的,所以需要開更多的記憶體。而 4 倍這個數字可以通過比較複雜的數學推導得出來。這裡不做過多的推導了。 所以,整個建樹的過程,時間複雜度是 o(n)。 線段樹入門
線段樹 interval tree 是把區間逐次二分得到的一樹狀結構,它反映了包括歸併排序在內的很多分治演算法的問題求解方式。上圖是一棵典型的線段樹,它對區間 1,10 進行分割,直到單個點。這棵樹的特點 是 1.每一層都是區間 a,b 的乙個劃分,記 l b a 2.一共有log2l層 3.給定乙...
線段樹入門
學習下 線段樹的入門級 總結 線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。對於線段樹中的每乙個非葉子節點 a,b 它的左兒子表示的區間為 a,a b 2 右兒子表示的區間為 a b 2 1,b 因此線段樹是平衡二叉樹,最後的子節點數目為...
線段樹 入門
首先線段樹形象來說就是將陣列看成乙個線段,然後不斷的進行分割,儲存在樹中的不同節點上,有點類似於b 樹的定義吧 觀察上圖,首先將整個陣列的某種資訊 最大值或者最小值等 儲存在根節點,對應 1,8 然後對 1,8 線段進行平分,得到 1,4 和 5,8 兩個線段,掛在樹的第二層。這樣節點2儲存了陣列中...