线段树
什么是线段树?
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
如果区间有n个元素,数组表示需要有多少个节点?
需要4n个的空间
我们的线段树不考虑添加元素,即区间固定
使用4n的静态空间即可
创建线段树的相关代码
package SegmentTree;
public class SegmentTree<E> {
private E[] tree;
private E[] data;
private Merger<E> merger;
public SegmentTree(E[] arr,Merger<E> merger) {
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];
bulidSegmentTree(0,0,data.length - 1);
}
//在treeIndex的位置创建表示区间[l....r]的线段树
private void bulidSegmentTree(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;
bulidSegmentTree(leftTreeIndex, l,mid);
bulidSegmentTree(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;
}
}
线段树中的区间查询
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;
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);
}
测试代码
public class Main {
public static void main(String[] args) {
Integer[] nums = {-2,0,3,-5,2,-1};
SegmentTree<Integer> segTree = new SegmentTree<>(nums, (a,b) -> a+b);
System.out.println(segTree.query(0, 2));
System.out.println(segTree.query(2, 5));
System.out.println(segTree.query(0, 5));
}
}