【Java】线段树对数组更新及区间求和的原理和应用

线段树对数组更新求区间和的原理和应用

实例题目:

 题目分析:

如果我们直接修改数组下标并且使用前缀和的形式求区间和,那么每修改一次,那么对应数组下标后的前缀和都要修改,那么我们的时间复杂度约为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.测试结果

 希望本篇对你有所帮助!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值