爲什麼要使用線段樹?
實質 : 基於區間的統計查詢
一個節點存儲的是一個區間相應的和
public class SegmentTree<E> {
private E[] tree;
private E[] data;
private Merger<E> merger;
public SegmentTree(E[] arr) {
this.merger = merger;
data = (E[]) new Object[arr.length];
for (int i = 0; i < arr.length; i++)
data[i] = arr[i];
tree = (E[]) new Object[4 * arr.length];
buildSegmentTree(0, 0, arr.length - 1);
}
// 在treeIndex的位置創建表示區間[l...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) / 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 int getSize() {
return data.length;
}
public E get(int index) {
if (index < 0 || index >= data.length)
throw new IllegalArgumentException("Index is illegal.");
return data[index];
}
// 返回完全二叉樹的數組表示中,一個索引所表示的元素的左孩子節點的索引
private int leftChild(int index) {
return 2 * index + 1;
}
// 返回完全二叉樹的數組表示中,一個索引所表示的元素的右孩子節點的索引
private int rightChild(int index) {
return 2 * index + 2;
}
// 返回區間[queryL, queryR]的值
public E query(int queryL, int queryR){
if(queryL < 0 || queryL >= data.length ||
queryR < 0 || queryR >= data.length || queryL > queryR)
throw new IllegalArgumentException("Index is illegal.");
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){
if(l == queryL && r == queryR)
return tree[treeIndex];
int mid = l + (r - l) / 2;
// treeIndex的節點分爲[l...mid]和[mid+1...r]兩部分
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);
E leftResult = query(leftTreeIndex, l, mid, queryL, mid);
E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
return merger.merge(leftResult, rightResult);
}
}