码迷,mamicode.com
首页 > 其他好文 > 详细

线段树——持续更新辣

时间:2015-12-01 18:10:33      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:

一:线段树的基本概念

1:概述

线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,基本能保持每个操作的复杂度为O(log n)
性质:假设某节点对应的区间是[a,b],设c=(a+b)/2,则左子树对应的区间是[a,c],右子树对应的区间是[c+1,b],线段树空间复杂度为O(n);

2:维护点及点修改的线段树

(1)构造线段树

函数:build(int begin,int end,int node) //构造一个线段树OwO

每个节点维护区间内最小值

主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后把左右较小者赋给当前的节点

技术分享
int array[MAX_N];
int segtree[4*MAX_N+10];
void build(int node, int begin, int end)    
{    
    if (begin == end)    
        segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */  
    else    
    {     
        /* 递归构造左右子树 */   
        build(2*node, begin, (begin+end)/2);    
        build(2*node+1, (begin+end)/2+1, end);   
           
        /* 回溯时得到当前node节点的线段信息 */    
        if (segTree[2 * node] <= segTree[2 * node + 1])    
            segTree[node] = segTree[2 * node];    
        else    
            segTree[node] = segTree[2 * node + 1];    
    }    
}  
View Code

 

(2)查询最小值

函数:Query(int begin,int end,int node,int left,int right) //查询[begin,end]的最小值,后面3个参数是在递归时为了计算方便而使用的.调用的时候用填0,0,n;

//如:我要查询[600,6000]的最小值,就用Query(600,6000,0,0,n);

主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息

技术分享
int Query(int begin,int end,int node,int left,int right)//查询[begin,end]的最小值    
{   
    int leftQ, rightQ;
    //如果完全不相交,则返回一个充分大的数INF,这里推荐用INT_MAX    
    if (left > end || right < begin)  return INF;    
    /*  如果完全包含  */    
    /*  返回当前节点的值  */  
    if (begin >= left && end <= right)    
        return segTree[node];    
    
    /*  比较左子树和右子树的最小值  */     
    leftQ = Query(begin,(begin+end)/2,2*node,left,right);   
    rightQ = Query((begin+end)/2+1,end,2*node+1,left,right);    
    
    /*  返回这个值  */ 
    return min(leftQ,rightQ);    
}  
View Code

(3)单节点更新

函数:void Updata(int begin, int end,int node, int ind, int add) //把编号为node的代表[begin,end]区间的节点加上add

技术分享
void Updata(int begin,int end,int node,int ind,int add)/*单节点更新*/    
{    
    //如果当前是叶子节点,则直接更新 
    if(begin==end)    
    {    
        segTree[node]+=add;    
        return;    
    }    
    int m=(left+right)>>1;  //‘>>1‘相当于除以2 
    if(ind<=m)Updata(left,m,node*2,ind,add);    //二分搜索 
    else Updata(m+1,right,node*2+1,ind,add);    //同样的二分搜索 
    /*更新父节点*/    
    segTree[node]=min(segTree[node*2],segTree[node*2+1]);     
         
}   
View Code

3.维护区间及区间修改的线段树

(1)概述

给出一个n个元素的数组A1,A2,...,An,你的任务是设计一个数据结构,支持以下两种操作。

  Add(L,R,v).把AL,AL+1,...,AR的值全部增加v。

  Query(L,R):计算子序列AL,AL+1,...,AR的元素和、最小值和最大值。

(2)构造线段树

函数:void maintain(int o,int L,int R) //构造一个节点O,对应[L,R]区间

技术分享
int minv[MAX_N],maxv[MAX_N],sumv[MAX_N],addv[MAX_N];  
void maintain(int o,int L,int R)
{
     int lc=o*2,rc=o*2+1; //左子树OvO和右子树OwO 
     if(R>L)
     {
         sumv[o]=sumv[lc]+sumv[rc];//考虑左右子树
         minv[o]=min(minv[lc],minv[rc]);
         maxv[o]=max(maxv[lc],maxv[rc]);
     }
     minv[o]+=addv[o];//考虑add操作
     maxv[o]+=addv[o]; 
     sumv[o]+=addv[o]*(R-L+1);
}
View Code

(3)操作1.add

函数:void update(int o,int L,int R) //add操作 

递归访问到的结点全部要调用maintain函数更新

技术分享
void update(int o,int L,int R)  //add操作 
{
     int lc=o*2,rc=o*2+1;
     if(yl<=L && y2>=R) addv[o]+=v;//递归边界,累加边界的add值     
     else
     {
         int M=L+(R-L)/2;
         if(yl<=M) update(lc,L,M);
         if(y2>M)  update(rc,M+1,R); 
     }
     maintain(o,L,R); //递归结束前重新计算本结点的附加信息
}
View Code

(4)操作2.Query

函数:void query(int o,int L,int R,int add)

把查询区间递归分解为若干不相交子区间,把各个子区间的查询结果加以合并,但需要注意的是每个边界区间的结果不能直接用,还得考虑祖先结点对它的影响。为了方便,我们在递归查询函数中增加一个参数,表示当前区间的所有祖先结点add值之和 

技术分享
int _min,_max,_sum;       //全局变量,目前位置的最小值、最大值和累加和
void query(int o,int L,int R,int add)  
{
     if(yl<=L && y2>=R)//递归边界:用边界区间的附加信息更新答案
     {
         _sum+=sumv[o]+add*(R-L+1);
         _min=min(_min,minv[o]+add);
         _ max=max(_max,maxv[o]+add);
     }
     else  //递归统计,累加参数add  
     { 
         int M=L+(R-L)/2;
         if(yl<=M) query(o*2,L,M,add+addv[o]);
         if(y2>M)  query(o*2+1,M+l,R,add+addv[o]);
     }
}
View Code

未完待续。。。



 

线段树——持续更新辣

标签:

原文地址:http://www.cnblogs.com/Kong-Ruo/p/5010804.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!