一。线段树(segment tree)
线段树(segment tree)是用来存放给定区间(segment, or interval)内对应信息的一种数据结构。
线段树是一颗近似的完全二叉树,每个节点代表一个区间,节点的权值是该区间的最小值。根节点是整个区间。
每个节点的左孩子是该节点所代表的的区间的左半部分,右孩子是右半部分。
为方便起见,如果区间长度为奇数,则左孩子为较长的半部分。
通过线段树,我们可以用O(logn)的时间复杂度完成查询和更新操作。
1.1最小值/max 线段树
0,1,2,3,4,5,6,7 index
1,5,3,7,3,2,5,7 array
0,1,2,3,4,5,6,7 index
1,5,3,7,3,2,5,7 array
private void buildTree(int[] nums) {
for (int i = n, j = 0; i < 2 * n; i++, j++)
tree[i] = nums[j];
for (int i = n - 1; i > 0; --i)
tree[i] = Math.min(tree[i * 2], tree[i * 2 + 1]); //min segment tree
// tree[i] = tree[i * 2] + tree[i * 2 + 1]; //sum range segment tree
}
segmentTree[1] = arr[0:8)
segmentTree[2] = arr[0:4)
segmentTree[3] = arr[4:8)
segmentTree[4] = arr[0:2)
segmentTree[5] = arr[2:4)
segmentTree[6] = arr[4:6)
segmentTree[7] = arr[6:8)
segmentTree[8] = arr[0]
segmentTree[9] = arr[1]
segmentTree[10] = arr[2]
segmentTree[11] = arr[3]
segmentTree[12] = arr[4]
segmentTree[13] = arr[5]
segmentTree[14] = arr[6]
segmentTree[15] = arr[7]
序列{5,9,7,4,6,1},length=6, mid=(left+right)/2
这棵树分左右两边,[0,2], [3,5]
[0,2]继续分,[0,1], [2,2]
[0,1]继续分,[0,0], [1,1]… …
1.2 区间和线段树 sum range
有一个数组[1, 3, 5, 7, 9, 11],线段树长这个样子:
reference:黄浩杰讲解线段树(segment tree)
reference:Segment tree in Java
import java.util.Arrays;
public class SegmentTree {
int[] tree;
SegmentTree(int n){
tree = new int[n];
}
public void build(int[] arr,int nodeIdx,int start,int end){
if(start==end){
tree[nodeIdx]=arr[start];
}else{
int mid=(start+end)/2;
build(arr,2*nodeIdx+1,start,mid);//left tree_nodeIdx
build(arr,2*nodeIdx+2,mid+1,end);//right tree_nodeIdx
tree[nodeIdx]=tree[2*nodeIdx+1]+tree[2*nodeIdx+2];
}
}
//index,value
public void update(int[] arr,int nodeIdx,int index,int value,int start,int end){
//find it
if(start==end){
arr[index]=value;
tree[nodeIdx]=value;//?+=
}else{
int mid=(start+end)/2;
if(start<=index && index<=mid){
//in left tree
update(arr,2*nodeIdx+1,index,value,start,mid);
}else{
update(arr,2*nodeIdx+2,index,value,mid+1,end);
}
tree[nodeIdx] = tree[2*nodeIdx+1] + tree[2*nodeIdx+2];
}
}
// 0 1 2 3 4 5
//{1,3,5,7,9,11};
//query sum in range [2--5]
public int query(int nodeIdx,int start,int end,int left,int right){
//out of the arr bounds:
if(right < start || left > end){
return 0;
}
if(left <= start && end <= right){
//all in left tree bounds ||all in right tree bounds
return tree[nodeIdx];
}
//both in left & right tree
int mid = (start + end)/2;
int p1 = query(2*nodeIdx+1, start, mid, left, right);
int p2 = query(2*nodeIdx+2, mid+1, end, left, right);
return p1 + p2;
}
public static void main(String[] args) {
int [] arr={
1,3,5,7,9,11};
int n=arr.length;
int height=(int) (Math.log(n)/Math.log(2))+ 1;
int tree_nodes = (int) Math.pow(2, height+1);
SegmentTree s=new SegmentTree(tree_nodes);
s.build(arr,0,0,5);
for(int i = 0; i < tree_nodes; i++){
System.out.print(s.tree[i] + " ");
}
System.out.println("\n"+"arr: "+ Arrays.toString(arr));
System.out.println("\n"+"Query result: "+s.query(0, 0, n-1, 0,2));
s.update(arr,0,1,2,0,n-1);
for(int i = 0; i < tree_nodes; i++){
System.out.print(s.tree[i] + " ");
}
System.out.println("\n"+"arr: "+ Arrays.toString(arr));
System.out.println("\n"+"Query result: "+s.query(0, 0, n-1, 0,2));
}
}
class NumArray {
int[] tree;
int n;
public NumArray(int[] nums) {
if (nums.length > 0) {
n = nums.length;
tree = new int[n * 2];
buildTree(nums);
}
}
private void buildTree(int[] nums) {
for (int i = n, j = 0; i < 2 * n; i++, j++)
tree[i] = nums[j];
for (int i = n - 1; i > 0; --i)
tree[i] = tree[i * 2] + tree[i * 2 + 1];
}
void update(int pos, int val) {
pos += n;//pos=2, pos=10
tree[pos] = val;
while (pos > 0) {
int left = pos;
int right = pos;
if (pos % 2 == 0) {
right = pos + 1;
} else {
left = pos - 1;
}
// parent is updated after child is updated
//tree[10/2] /tree[5/2] /tree[2/2]
tree[pos / 2] = tree[left] + tree[right];
pos /= 2;
}
}
public int sumRange(int l, int r) {
// get leaf with value 'l'
l += n;
// get leaf with value 'r'
r += n;
int sum = 0;
while (l <= r) {
if ((l % 2) == 1) {
</