一. 線段樹的特點
-
平衡二叉樹:最大的深度 和最小的深度 差最大爲1。
-
一種二叉搜索樹。它將一段區間劃分爲若干單位區間,每一個節點都儲存着一個區間。它功能強大,支持區間求和,區間最大值,區間修改,單點修改等操作。
處理問題: 區間查詢 ,區間更新 -
線段樹 是以空間換時間的處理,開啓4N的空間
如果區間有n個元素,數組表示需要有多少節點? 4n的空間,我們的線段樹不考慮添加元素,即空間固定,使用4n的靜態空間即可。可能有浪費,最壞情況,浪費2n-1的空間,空間換時間。0 層 : 1
1 層 : 2
2 層 : 4
3 層 : 8
。。。
h 層 : 2^h -1
對滿二叉樹: h層,一共·2^h -1 個節點(大約2 ^ h)
最後一層 (h-1)層,有2^(h-1) -1 個節點
最後一層的節點數大致等於前面所有層節點之和。
如果 n = 2^k ,只需要2n區間
最壞情況,如果n = 2^k + 1 ,需要4n的空間。
如果使用鏈表,可以避免浪費空間。
二. 線段樹的初始化
僞代碼如下:
/**
* 在treeIndex的位置創建表示區間[l...r]的線段樹
* 遞歸實現 這裏有些不好理解,需要手動畫畫
*
* @param treeIndex
* @param l
* @param r
*/
private void buildSegmentTree(int treeIndex, int l, int r) {
if (l == r) {
tree[treeIndex] = data[l];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
//創建左右子樹
int mid = l + (r - l) / 2;
//l,mid; mid+1,r;遞歸
buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid + 1, r);
tree[treeIndex] = merge.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}
三. 區間查詢
僞代碼如下:
/**
* 在以treeIndex爲根的線段樹中【l,r】的範圍裏,搜索區間【queryL,queryR】的值
*
* @param treeIndex 初始爲0
* @param l 初始爲0
* @param r 初始爲數組最大索引
* @param queryL 左區間
* @param queryR 右區間
* @return
*/
private E query(int treeIndex, int l, int r, int queryL, int queryR) {
if (l == queryL && r == queryR) {
return tree[treeIndex];
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if (queryL >= mid + 1) {
return query(rightTreeIndex, mid + 1, r, queryL, queryR);
} else if (queryR <= mid) {
return query(leftTreeIndex, l, mid, queryL, queryR);
} else {
//一部分左孩子,一部分右孩子上
E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
return merge.merge(leftResult, rightResult);
}
}
四. 區間更新
都是採用遞歸的方式更新
//在以treeIndex爲跟的線段樹種更新index的值爲e 類似二分搜索樹中的更新
public void set(int treeIndex, int l, int r, int index, E e) {
if (l == r) {
tree[treeIndex] = e;
return;
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if (index >= mid + 1) {
set(rightTreeIndex, mid + 1, r, index, e);
} else {
set(leftTreeIndex, l, mid, index, e);
}
tree[treeIndex] = merge.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}