專欄 | 九章演算法
網址 | www.jiuzhang.com
1. 線段樹入門問題
1.1 題目描述
給出數列[1 4 2 3],求給定區間的最大值
例:query(0,1) = 4 query(2,3) = 3 query(0,3) = 4**
1.2 解題思路分析
一道題可不可以用線段樹來做,基本是看這道題的操作有沒有區間的性質。也就是在一個區間上的操作是否可以轉化為兩個子區間上的操作。
從這題可以看出:
區間(a,b)的最大值和區間(b,c)的最大值中,取較大的就是區間(a,c)的最大值**複製程式碼
很明顯可以看出這個操作是具有區間的性質。
1.3 套路
幾種常見的線段樹的套路
1.求區間的和,積,最大值,最match小值,gcd等
2.以當前結點的值作為結點處理,比如給出N個數,再給一個數,問比這個數大的有多少個(當然,用樹狀陣列可以很好的處理,但有修改時,線段樹就會方便很多)
3.區間加減同一個值,或者區間同時賦一個值(在面試中,通常不會問到,演算法競賽中會經常用到)複製程式碼
2. 構造
一顆線段樹的構造就是根據區間的性質的來構造的
區間劃分大概就是上述的區間劃分。可以看出每次都將區間的長度一分為二,數列長度為n,所以線段樹的高度是log(n),這是很多高效操作的基礎。
上述的區間儲存的只是區間的左右邊界。我們可以將區間的最大值加入進來。
區間的第三維就是區間的最大值
加這一維的時候只需要在建完了左右區間之後,根據左右區間的最大值來更新當前區間的最大值即可。
因為每次將區間的長度一分為二,所有創造的節點個數為
n + 1/2 * n + 1/4 * n + 1/8 * n + ...
= (1 + 1/2 + 1/4 + 1/8 + ...) * n
= 2n複製程式碼
所以構造線段樹的時間複雜度和空間複雜度都為O(n)
2.1 程式碼1
線段樹構造程式碼模板
3. 區間查詢
構造線段樹的目的就是為了更快的查詢。
給定一個區間,要求區間中最大的值。
線段樹的區間查詢操作就是將當前區間分解為較小的子區間,然後由子區間的最大值就可以快速得到需要查詢區間的最大值。
query(1,3) = max(query(1,1),query(2,3)) = max(4,3) = 4
上述例子將(1,3)區間分為了(1,1)和(2,3)兩個區間,因為(1,1)和(2,3)區間的最大值事先已經記錄好了,所以直接拿來用就可以了。
3.1 層數
每一層最多查詢4個區間
考慮長度為16的序列構造成的線段樹,區間(0,15)。查詢區間(1,14)。
第一層會查詢 (1,7) (8,15)
第二層會查詢 (1,3) (4,7) (8,11) (12,14)
第三層會查詢 (1,1) (2,3) (12,13) (14,14)
第四層會查詢 (1,1) (14,14)複製程式碼
因為是連續的,中間那部分會直接算出來,然後兩邊的兩端繼續往下,還是4個區間。
因為線段樹的高度為log(n),所以查詢的時間複雜度為O(log(n))
3.2 程式碼2
線段樹區間查詢模板
3.3 單點更新
線段樹構造程式碼模板
如何把這種變化體現到線段樹中去?
例如,將序列中的第4個點更新為5,要變動3個區間中的值,分別為[3,3],[2,3],[0,3]
可以這樣想,改動一個節點,與這個節點對應的葉子節點需要變動。因為葉子節點的值的改變可能影響到父親節點,然後葉子節點的父親節點也可能需要變動。
所以需要從葉子節點一路走到根節點,去更新線段樹上的值。因為線段樹的高度為log(n),所以更新序列中一個節點的複雜度為log(n)。
3.4 程式碼3
線段樹單點更新模板
如果需要區間的最小值或者區間的和。和構造的時候同理。
4. 相關線段樹面試題 Online Judge
4.1 線段樹構造
Segment Tree Build
Segment Tree BuildII
4.2 線段樹單點更新
4.3 線段樹區間查詢
Segment Tree Query
Segment Tree QueryII
4.4 線段樹綜合
Interval Minimum Number
Interval Sum
Interval SumII
Count of Smaller Number
Count of Smaller Number Before itself
歡迎關注我的微信公眾號:九章演算法(ninechapter)。
精英程式設計師交流社群,定期釋出面試題、面試技巧、求職資訊等