线段树的建立与应用---区间求和

 
package arithmetic;

class Segment
{
	int left;
	int right;
	int count=0;
	int sum =0;
}

public class SegmentTree {
	
	public static int num[] = new int[5000];
	public static void main(String args[])
	{
	
		Segment tree[] = new Segment[5000];
		for(int i=0;i<tree.length;i++)
			tree[i] = new Segment();
		/*create(tree,0,7,1);
		for(int i=1;i<=15;i++)
		{
			System.out.println("["+tree[i].left+" "+tree[i].right+"]");
		}*/
		// insert(tree,4,4,1);
		for(int i=1;i<=7;i++)
			num[i]=i;
		build(tree,1,7,1);
		for(int i=1;i<=15;i++)
		{
			System.out.println("["+tree[i].left+" "+tree[i].right+"] "+tree[i].sum);
		}
		System.out.println(query(tree,1,2,1));
		
		// 修改
		modify(tree,1,10,1);
		for(int i=1;i<=15;i++)
		{
			System.out.println("["+tree[i].left+" "+tree[i].right+"] "+tree[i].sum);
		}
		
	}
	
	/*
	 * 		线段树是一个完全二叉树,故其叶子结点为满足:左孩子=2*n  右孩子=2*n+1
	 * */
	public static void create(Segment[]tree,int l,int r,int n)  //线段树是一个静态二叉树结构
	{
		
		
			if(l==r)		//如果线段的两个端点已经相等的话,把它插入到里面就可以停止了,这样线段树的叶子结点就是 [1-1][2-2]这种形式的了,因为在插入线段的时候,如果要插入的线段不能洽好在线段树的一侧的话,那么就要把该线段分成两半插入到线段树中,那么在分隔的原理是:根据当前结点的中点进行分隔,当前结点的中点为mid ,那么就将线段[a-b]分成 [a-mid] 插入线段的左孩子,[mid-b]插入线段的右孩子中,但在分隔的过程中,如果分开的一段洽好是a==mid ,那么就会出现两个值都一样的情况,所以我们在建立线段树的时候,要建立到底,直到把所有的点都建立出来 
			{
				tree[n].left=l;
				tree[n].right=r;
				return ;	
			}
			tree[n].left=l;
			tree[n].right=r;
			int mid = (r+l)/2;
			create(tree,l,mid,n*2);
			create(tree,mid+1,r,n*2+1);
			
	}
	/*
	 * 			线段树的插入
	 * */
	public static void insert(Segment[] tree,int l,int r,int n)
	{

		System.out.println(n);
		if(l == tree[n].left && r == tree[n].right )
		{
			tree[n].count++;
			return ;
		}
		int mid = (tree[n].left + tree[n].right)/2;
		if(r<=mid) // 插入到左孩子中去
			insert(tree,l,r,2*n);
		else					//一定要加 else , r = = l 的情况出如果不判断会出现错误 
		if(l>=mid)// 插入到右孩子中去
			insert(tree,l,r,2*n+1);
		else		//将 l ,r 分成两个线段
		{
			insert(tree,l,mid,2*n);
			insert(tree,mid+1,r,2*n+1);
		}
			
	}
	
	/*		--------------------------------------------应用-------------------------------------------------------
	 * 					下面写一个区间求和的算法,需求:N个点,每个点的有一个值,求任意两个点之间的和
	 * 
	 * 					1. 首先创建一个线段树,每个结点的线段保存是两人上结点之间所有结点的和
	 * 					2. 下面查询任意两个点之间的和			如果 [2-5] 那么就要求出  2 3 4 5 上点的和
	 * */
	
	
	/*
	 * 		l - r 代表区间
	 * 		返回 数组 num [l -r ]区间内的和
	 * */
	public static  int  build(Segment tree[],int l,int r,int n)
	{
		tree[n].left=l;
		tree[n].right=r;
		if(l==r)
		{
			return tree[n].sum=num[l];
		}
		
		int mid = (l+r)>>1;
		return tree[n].sum = build(tree,l,mid,2*n)+ build(tree,mid+1,r,2*n+1);
		
	}
	/*
	 * 			# l-r 表示区间
	 * 			返回区间内的和
	 * 
	 * */
	public static int query(Segment tree[],int l,int r,int n)
	{
		if(l==tree[n].left && r==tree[n].right)
			return tree[n].sum;
		int mid = (tree[n].left+tree[n].right)>>1;
		if(r<=mid)
			return query(tree,l,r,n<<1);
		else
			if(l>=mid)
				return query(tree,l,r,n<<1|1);
			else
				return query(tree,l,mid,n<<1)+query(tree,mid+1,r,n<<1|1);
	}
	/*
	 * 		id  要修改的结点号
	 * 		sum 要修改为多少人
	 * */
	public static void modify(Segment tree[],int id,int sum,int n) //是其一个点的人数,只要把其本身和所有的孩子改了即可,
	{
		tree[n].sum+=sum;
		if(tree[n].left == tree[n].right)  
			return ;
		int mid = (tree[n].left+tree[n].right)>>1;
		if(id<=mid)							//根据修改的编号判断是修改左孩子还是右孩子,因为不可能现时修改两个孩子
			modify(tree,id,sum,n<<1);
		else
			modify(tree,id,sum,n<<1|1);
	}
	
	/*
	 * 				些事例为求任意区间内的和,所以线段树维护的应该是区间内的和 
	 * 
	 * */

	
	

}

你可能感兴趣的:(线段树的建立与应用---区间求和)