数据结构-线段树
作者:互联网
数据结构-线段树
概述线段树是一颗平衡的二叉搜索树,他以空间换区时间,让线性查找加速log级别的查找,用到的算法主要是二分搜索和递归。
例如:有数组data[]={1,2,3,4}, 我有一个需求,我需要频繁的查找区间i~j的sum和。这里先给出两个解决方案:
如果使用最普通的算法遍历,那么查找和更新的复杂度为O(n)。
当然你还可以使用动态规划,定义dp数组为dp[i][j] ,意义为从i~j的sum和,但是更新某个数据的复杂度也是O(n)。
又或者定义dp数组为dp[i], 意义为从0~i的sum和,同理更新的复杂度也是O(n)。
线段树图示
如果我们有办法构建图示这样的树,那么查找和更新都可以log(n)搞定。
首先我们考虑一个问题,构建这样的一棵树所需要的树节点个数与data的长度有什么关联,也就是说我的树节点应该开多少空间,第一棵树是一颗满二叉树,因为data正好是2的k次方,这样树的节点个数为2的k次方-1,最后一层的个数正好是上面所有层的节点个数+1,所有对于这种满二叉树节点个数开2倍data.length足够了,对于右边图,开4倍data.length足够了
buildSegmentTree图示
public class SegmentTree<E> {
private E[] data;
private E[] tree;
private Merger<E> merger;
public SegmentTree(E[] pData,Merger<E> pMerger){
if(null==pData||pData.length==0)
throw new RuntimeException("data must not empty");
if(null==pMerger)
throw new RuntimeException("merger must not empty");
merger = pMerger;
data = (E[]) new Object[pData.length];
for(int i=0;i<pData.length;++i){
data[i] = pData[i];
}
tree = (E[]) new Object[4*pData.length];
buildSegmentTree(0,0,data.length-1);
}
//以treeIndex为根构建从left到right的线段树
private void buildSegmentTree(int treeIndex, int left, int right) {
if(left==right){
tree[treeIndex] = data[left];
return;
}
int mid = left+(right-left)/2;
int leftIndex = leftIndex(treeIndex);
int rightIndex = rightIndex(treeIndex);
buildSegmentTree(leftIndex,left,mid);
buildSegmentTree(rightIndex,mid+1,right);
tree[treeIndex] = merger.merge(tree[leftIndex],tree[rightIndex]);
}
//查找
public E query(int queryL,int queryR){
if(queryL>queryR)
throw new RuntimeException("queryL must < queryR");
checkIndex(queryL);
checkIndex(queryR);
return query(0,queryL,queryR,0,data.length-1);
}
//以treeIndex为根从范围l-r查找queryL到queryR的线段树的merger值
private E query(int treeIndex,int queryL,int queryR,int l,int r){
//递归结束条件
if(queryL==l&&queryR==r){
return tree[treeIndex];
}
int mid = l+(r-l)/2;
int leftIndex = leftIndex(treeIndex);
int rightIndex = rightIndex(treeIndex);
//左边
if(queryR<=mid){
return query(leftIndex,queryL,mid,l,mid);
}
//右边
else if(queryL>mid){
return query(rightIndex,mid+1,queryR,mid+1,r);
}else{
return merger.merge(query(leftIndex,queryL,mid,l,mid),query(rightIndex,mid+1,queryR,mid+1,r));
}
}
//更新
public void set(int index,E e){
checkIndex(index);
data[index] = e;
set(0,0,data.length-1,index,e);
}
private 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 leftIndex = leftIndex(treeIndex);
int rightIndex = rightIndex(treeIndex);
//右边更新
if(index>mid){
set(rightIndex,mid+1,r,index,e);
}else{
set(leftIndex,l,mid,index,e);
}
tree[treeIndex] = merger.merge(tree[leftIndex],tree[rightIndex]);
}
private void checkIndex(int index){
if(index<0||index>=data.length){
throw new RuntimeException("index is illegel");
}
}
public int leftIndex(int index){
return 2*index+1;
}
public int rightIndex(int index){
return 2*index+2;
}
}
public interface Merger<E> {
E merge(E a,E b);
}
public class TestSegment {
public static void main(String[] args) {
Integer[] data = new Integer[]{1,2,3,4};
SegmentTree<Integer> tree = new SegmentTree<>(data,(a,b)->{return a+b;});
System.out.println(tree.query(1, 3));
tree.set(2,10);
System.out.println(tree.query(1, 3));
}
}
标签:index,treeIndex,int,线段,tree,mid,数据结构,data 来源: https://blog.51cto.com/u_12856278/3042443