线段树对数组更新求区间和的原理和应用
实例题目:
题目分析:
如果我们直接修改数组下标并且使用前缀和的形式求区间和,那么每修改一次,那么对应数组下标后的前缀和都要修改,那么我们的时间复杂度约为O(n)
使用线段树的形式来解决,时间复杂度降为O(log n)
求解原理:
1.数组更新:如图所示这是数组num[]={2,4,5,7,8,9}构成的线段树,那么其中包括11个树结点,如果要执行修改操作时只需要修改部分线段节点的线段和即可,如将num[2]改为6,则只需要更改【0,5】【0,2】【2】这三个线段结点的线段和即可。
2.数组求区域和:如果求数组下标2-4的区间和则只需返回【2】【3,4】两个结点的线段和的和即可。
具体代码来实现:
1.设计树节点类
线段结点需要储存:线段和、线段对应数组下标的始末位置、线段的左右分段结点(类似链表)
public class treenode{
int sum;//数段和
int start,end;//数段始末位置
treenode left,right;//数段左右结点
private treenode(int start,int end) {//构造方法
this.start=start;
this.end=end;
}
2.构造线段数方法
采用递归构造从树的顶端开始从上往下构造
public static treenode buildtree(int[] nums, int start, int end) {//递归构造线段树
if(start>end) {//起始位置大于结束位置,无法构造,直接返回空
return null;
}
else if(start==end) {//始末位置相等,线段和为一个数也就等于对应数组下标的值
treenode node=new treenode(start, end);//先构造对应节点
node.sum=nums[start];//写入线段和
return node;
}else {//起始位置小于结束位置,线段和=左线段和+右线段和
treenode node=new treenode(start, end);//先构造对应节点
int mid=start+(end-start)/2;//从中间区分,从而构造左右结点
node.left=buildtree(nums,start,mid);//构造左结点
node.right=buildtree(nums,mid+1,end);//构造右结点
node.sum=node.left.sum+node.right.sum;//写入线段和=左线段和+右线段和
return node;
}
// TODO Auto-generated method stub
}
3.线段树的更新方法
递归更新 从下向上更新
private void update(treenode node, int index, int val) { //更新线段树 ,递归修改
if(node.start==node.end) {//线段节点始末位置相同,直接更改线段和
node.sum=val;
}else{//线段节点左右不相同,线段和=左线段和+右线段和
int mid=node.start+(node.end-node.start)/2;//求线段中间数,用来区分修改左线段还是右线段
if(index<=mid) {//修改左线段
update(node.left,index,val);
}else {//修改右线段
update(node.right,index,val);
}
node.sum=node.left.sum+node.right.sum;//修改线段和
}
// TODO Auto-generated method stub
}
4.数组求和方法
递归获取各线段和
private int intervalsum(treenode root, int left, int right) {//求线段树left-right区间值 ,递归求和
if(root.start==left && root.end==right) //线段起始位置=left且结束位置=right,直接返回线段和
return root.sum;
else {
int mid=root.start+(root.end-root.start)/2;
if(right<=mid)//目标线段在该线段的左线段
return intervalsum(root.left,left,right);
else if(left>mid)//目标线段在该线段的右线段
return intervalsum(root.right,left,right);
else if(left<=mid&&right>mid)//目标线段横跨该线段的左右线段
return intervalsum(root.left,left,mid)+intervalsum(root.right,mid+1,right);
}
return 0;
// TODO Auto-generated method stub
}
5.实例化测试
public static void main(String[] args) {
int[] num= {1,2,3,4,5,6,7};
treenode root=buildtree(num,0,num.length-1);//此时需要的下标值,所以需要length-1
System.out.println(root.intervalsum(root, 2, 4));
root.update(root, 3, 9);
System.out.println(root.intervalsum(root, 2, 4));
}
6.测试结果
希望本篇对你有所帮助!