線段樹要解決的問題是: 在乙個實時更新的動態陣列中查詢區間和(或廣義的區間狀態,如區間積,區間最大最小值).
若所查詢的陣列為靜態陣列,則此問題只需要使用陣列字首和
即能解決.
線段樹是一種平衡二叉樹,其每個節點儲存陣列中一段區間和
.其左子節點
儲存區間左半部分和
;其右子節點
儲存區間右半部分和
.
線段樹陣列的內容為[15, 3, 12, 1, 2, 7, 5, 0, 1, 0, 0, 3, 4]
,注意到陣列中對應完全二叉樹的下標9
和下標10
的節點不存在.
為了便於下標計算,我們的實際線段樹陣列下標是從1開始的,0位被浪費了.線段樹的遞迴構造: 若某節點儲存原陣列
nums[leftindex] ~ nums[rightindex]
區間陣列和,其左子節點儲存原陣列nums[leftindex] ~ nums[midindex]
區間陣列和,其右子節點儲存原陣列nums[midindex+1] ~ nums[rightindex]
區間陣列和.
線段樹開闢的陣列長度應為原陣列長度的四倍,證明如下:
若原陣列nums
的長度為n
,線段樹深度h
應為⌈lo
g2n+
1⌉\lceil log_n+1\rceil
⌈log2
n+1⌉
,則該完全二叉樹的陣列長度應為2h+
1⩽4n
2^\leqslant4n
2h+1⩽4
n.
private
int[
] segmenttree;
// 線段樹的結構
// 構造線段樹
private
void
buildsegmenttree
(int
nums)
// 構造線段樹上的segment[pos]節點,該節點儲存原nums陣列[leftindex,rightindex]部分的陣列和
private
intbuildsegmenttree
(int
nums,
int pos,
int leftindex,
int rightindex)
else
// 返回該節點對應的區間和
return segmenttree[pos]
;}
// 列印整顆線段樹
public
void
printsegmenttree()
// 先序遍歷列印線段樹第pos位為樹根的子樹,該節點儲存原nums陣列leftindex至rightindex部分的陣列和
private
void
printsegmenttree
(int pos,
int leftindex,
int rightindex,
int layer)
// 顯示樹根節點
system.out.
println
("["
+ leftindex +
" "+ rightindex +
"]: "
+ segmenttree[pos]);
// 若存在子樹,則遞迴列印左右子樹
if(leftindex < rightindex)
}
列印結果如下:
區間和的查詢應用二分思想,不斷將當前區間二分直到當前區間全部位於查詢區間內,這時將該節點所儲存的區間和
與其它所有位於查詢區間內的子區間和
相加得到總的查詢區間和
.
// 查詢原sum陣列qleftindex到qrightindex部分陣列和
public
intsumrange
(int qleft,
int qright)
// 在 `線段樹陣列中代表原sum陣列[leftindex,rightindex]區間內` 二分查詢 `原sum陣列[qleftindex,qrightindex]區間和`
private
intsumrange
(int pos,
int leftindex,
int rightindex,
int qleftindex,
int qrightindex)
else
if(qleftindex <= leftindex && qrightindex >= rightindex)
else
}
若更新了陣列某一位,則線段樹中包含該位的左右區間和也必然被更新.
從根節點(對應原nums
陣列[0, nums.len-1]
區間和)開始,不斷將其所有包含第i位
的子區間加上對應的偏移量.
// 將原陣列nums第i位的值改為val
public
void
update
(int i,
int val)
// 將線段樹中 包含原陣列第i位的所有區間和 加上乙個 增量delta
private
void
update
(int pos,
int i,
int delta,
int leftinndex,
int rightindex)
}
演算法學習筆記 線段樹
在樹狀陣列那篇部落格中,留下了乙個坑 區間修改區間查詢 樹狀陣列部落格傳送門 今天我們就要來解決這個問題 都很簡單 線段樹是一種可以較快維護滿足區間可加性區間資訊 如 區間和,區間積,區間最大最小等 的資料結構,其基本思想就是二分。注 區間可加性指一些可以通過子區間資訊合併維護的資訊,如區間最大就可...
演算法學習筆記 樹狀陣列 線段樹
支援單點修改和區間查詢兩個操作,單次操作o logn tr x 陣列存的是原陣列在區間 x lowbit x x 上的和 若要將乙個數x變為v,則將x x v,即加上v x 模板如下 建立樹狀陣列 for int i 1 i n i scanf d a i for int i 1 i n i add...
演算法學習筆記 14 離散化操作
離散化是一種輔助解決問題的操作,當問題中涉及的資料範圍非常大,但是實際使用到的資料是比較少的。並且問題的求解是和它範圍裡的其它資料有關係的,那麼可以將這些可能使用到的資料放到一起,排序去重,就將它們對映到了乙個新的較小的範圍裡。例如,下面幾個數是在 infty,infty 範圍裡的,實際用到的數 6...