線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。想要理解線段樹的**,最好先參考動態陣列實現最大堆實現最大堆的**,因為他們都是根據陣列進行構建的二叉樹.使用線段樹可以快速的查詢某乙個節點在若干條線段**現的次數,時間複雜度為o(logn)。而未優化的空間複雜度為2n,實際應用時一般還要開4n的陣列以免越界,因此有時需要離散化讓空間壓縮。
實現線段樹並實現更新,查詢:
package segmenttree;
/** * @author bestqiang
*/public
class
segmenttree
// 開4n的陣列以免越界
tree =
(e)new
object[4
* arr.length]
;buildsegmenttree(0
,0, data.length -1)
;}// 在treeindex的位置建立表示區間[l...r]的線段樹
private
void
buildsegmenttree
(int treeindex,
int l,
int r)
int lefttreeindex =
leftchild
(treeindex)
;int righttreeindex =
rightchild
(treeindex)
;// 下面這種取中間值的做法在取得數特別大的是後續可能會導致整形溢位的問題,所以不採用
// int mid = (l + r) / 2;
int mid = l +
(r - l)/2
;buildsegmenttree
(lefttreeindex, l, mid)
;buildsegmenttree
(righttreeindex, mid +
1, r)
;// 避免線段樹侷限性,所以設定乙個新的介面,融合器
tree[treeindex]
= merger.
merge
(tree[lefttreeindex]
, tree[righttreeindex]);
}// 獲取陣列的長度
public
intgetsize()
// 獲取指定位置的陣列值
public e get
(int index)
return data[index];}
// 返回完全二叉樹的陣列表示中,乙個索引說表示的元素的做孩子節點的索引
private
intleftchild
(int index)
// 返回完全二叉樹的陣列表示中,乙個索引所表示的元素的有孩子節點的索引
private
intrightchild
(int index)
// 返回區間[queryl, queryr]的值
public e query
(int queryl,
int queryr)
return
query(0
,0, data.length -
1, queryl, queryr);}
// 在以treeindex為根的線段樹[l...r]的範圍裡, 搜尋區間[queryl...queryr]的值
private e query
(int treeindex,
int l,
int r,
int queryl,
int queryr)
// 取中間值
int mid = l +
(r - l)/2
;// treeindex的節點分為[l...mid]和[mid+1...r]兩部分
int lefttreeindex =
leftchild
(treeindex)
;int righttreeindex =
rightchild
(treeindex)
;// 如果範圍某一側,直接在那一側進行查詢
if(queryl >= mid +1)
else
if(queryr <= mid)
// 如果範圍分布在樹的兩側,在兩側分別進行查詢
// 對左孩子,右孩子進行查詢
e leftresult =
query
(lefttreeindex, l, mid, queryl, mid)
; e rightresult =
query
(righttreeindex, mid +
1, r, mid +
1, queryr)
;// 合併左右查詢結果
return merger.
merge
(leftresult, rightresult);}
// 將index位置的值, 更新為e
public
void
update
(int index, e e)
data[index]
= e;
set(0,
0, data.length -
1, index, e);}
private
void
update
(int treeindex,
int l,
int r,
int index, e e)
int mid = l +
(r - l)/2
;// treeindex的節點分為[l...mid]和[mid+1...r]兩部分
int lefttreeindex =
leftchild
(treeindex)
;int righttreeindex =
rightchild
(treeindex);if
(index >= mid +1)
set(righttreeindex, mid +
1, r, index, e)
;else
// index <= mid
set(lefttreeindex, l, mid, index, e)
; tree[treeindex]
= merger.
merge
(tree[lefttreeindex]
, tree[righttreeindex]);
}@override
public string tostring()
res.
(']');
return res.
tostring();}}
因為具體不知道合併的條件和要求,所以建立合併介面,呼叫時指定行為即可:
/**
* @author bestqiang
*/public
inte***ce
merger
對線段樹進行測試:
/**
* @author bestqiang
*/public
class
main
; segmenttree
segmenttree =
newsegmenttree
<
>
(nums,
(a, b)
-> a + b)
;system.out.
println
(segmenttree.
query(0
,3));}}
輸出結果:
-4
測試成功! 樹的區間查詢與更新(線段樹)
原題 include include include include include include include include using namespace std define clr x memset x,0,sizeof x define ll long long define typ...
線段樹的構建和查詢
本系列函式引數表示為 k 節點儲存區間 l,r 的和,查詢 修改的點為 x 查詢 修改的區間為 x,y 我們從線段樹的定義可以知道,k 節點儲存 ls k 和 rs k 兩節點的和。所以,我們採用遞迴構建線段樹。當區間的左端點與右端點重合時,即為葉子結點。非葉子結點在回溯中計算出。void buil...
線段樹的建樹 更新 查詢操作
模板題sdut oj 3771 陣列計算機 description blue 有乙個神器的機器,這個機器可以讀入乙個陣列,並按照使用者要求快速地進行陣列的處理和計算,它支援如下兩種操作 操作 1 把陣列中第 p 個元素的值增加 v。操作 2 計算陣列中 l,r 區間內所有數的和。這個機器就是這麼的神...