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

来源:互联网 发布:阿里云华东1可用区 编辑:程序博客网 时间:2024/05/16 08:49
 
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);elseif(l>=mid)return query(tree,l,r,n<<1|1);elsereturn 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);elsemodify(tree,id,sum,n<<1|1);}/* * 些事例为求任意区间内的和,所以线段树维护的应该是区间内的和  *  * */}

原创粉丝点击