原数组[1,2,3,4,5]
线段树数组[15, 6, 9, 3, 3, 4, 5, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
简单的可以看出线段树满足二叉树性质;故有:
当前索引x2+1=左子索引;
当前索引x2+2=右子索引;
当前值=左子索引值+右子索引值;
当前索引表示原数组范围[l,r]的值:
mid=(l+r)/2;
左子索引表示范围[l,mid]
右子索引表示范围[mid+1,r]
叶子节点表示的范围是1,就是说表示原数组的一个索引的值
关键点:
叶子节点表示原数组的一个索引;
所以有:
叶子节点值=原数组[叶子节点范围的左/右边界点]
java实现:
1. 数组实现:
class SegmentTreeNode {
//data原数组,tree线段树数组
private int[] data;
int[] tree;
//利用原数组初始化线段树
public SegmentTreeNode(int[] arr) {
this.data = arr;
//最大容量4倍原数组长度
tree = new int[4 * arr.length];
buildSegmentTreeNode(0, 0, arr.length - 1);
}
//线段树初始化
private void buildSegmentTreeNode(int treeIndex, int l, int r) {
//当前节点是叶子节点
if (l == r) {
tree[treeIndex] = data[l];
return;
}
//mid当前范围中点;leftTreeIndex/rightTreeIndex当前节点左/右子节点
int mid = (l + r) >>> 1;
int leftTreeIndex = treeIndex * 2 + 1, rightTreeIndex = treeIndex * 2 + 2;
//递归建左右子树
buildSegmentTreeNode(leftTreeIndex, l, mid);
buildSegmentTreeNode(rightTreeIndex, mid + 1, r);
//非叶子节点更新节点值
tree[treeIndex] = tree[leftTreeIndex] + tree[rightTreeIndex];
}
//查询区间[queryL, queryR]的值
public int query(int queryL, int queryR) {
if (queryL < 0 || queryL >= data.length || queryR < 0 || queryR >= data.length || queryL > queryR) {
throw new IllegalArgumentException("QueryIndex is illegal");
}
return query(0, 0, data.length - 1, queryL, queryR);
}