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

浅谈线段树 - 数据结构

时间:2020-10-13 16:58:38      阅读:18      评论:0      收藏:0      [点我收藏+]

标签:bcb   遍历   amp   loaded   log   ima   ade   区间修改   lazy   

线段树

对于维护区间内的信息,我们可使用RMQ,但这种做法的缺点是无法快速修改,而线段树这种数据结构则可以实现实时的查询、修改(单点、区间)。

原理:

技术图片

线段树是一种二叉搜索树,对于每个节点,他代表区间L~R的信息,而其两个子节点分别代表L~mid、mid+1~R的信息。

建树:

只需要遍历到每个叶子节点时赋值,回溯过程中再对树进行调整

也可以进行N次单点修改,一般来说对总的时间复杂度影响不大

void build(int l,int r,int p)
{
   int mid=(l+r)>>1;
   if(l==r)
  {
       s[p]=v[l];
       return;
  }
   build(l,mid,lson);
   build(mid+1,r,rson);
}

区间查询:

若想查询某一区间信息,我们可从根节点遍历。当遍历到的节点被查询区间完全覆盖的时候,便返回信息。

在建树时,我们可以选择用堆式结构,或者动态开点。

如要查询1~8的信息:

节点1~10左右节点均有1~6的信息,向下查询

节点1~5被查询区间完全包含,返回该点信息

节点6~10的左区间包含查询区间信息,向左遍历

节点6~8被查询区间完全包含,返回该点区间信息

最后获得1~5、6~8的信息,再进行整合。

如求和:

int query_s(int l,int r,int x,int y,int p)
{
  int mid=(l+r)>>1;
  if(x<=l&&y>=r) return s[p];
  int ret=0;
  if(x<=mid) ret+=query_s(l,mid,x,y,lson);
  if(y>mid) ret+=query_s(mid+1,r,x,y,rson);
  return ret;    
}

此段代码中,我们选择把该区间的左右范围传入函数,这样可以节省空间与时间。

若使用堆式结构,那么lson = p<<1 , rson = p << 1 | 1 (p为该点的位置)

若使用动态开点,lson rson则为数组

单点修改:

对于单点修改,首先,我们要找到该点在线段树中对应的位置,再将其权值修改,在回溯的过程中修改沿途的节点。

若使用动态开点,我们则需要注意左右儿子节点是否被开出来过,这里我们用传引用的方式对p进行修改。

动态开点版:

void update(int l,int r,int x,int v,int &p)
{
   int mid=(l+r)>>1;
   if(p==0) p=++cnt;
   if(l==r)
  {
       m[p]=v;
       s[p]=v;
       return;
  }
  if(x<=mid) update(l,mid,x,v,lson[p]);
  else update(mid+1,r,x,v,rson[p]);
  m[p]=max(m[lson[p]],m[rson[p]]);
  s[p]=s[lson[p]]+s[rson[p]];
}

堆式结构版:

void update(int l,int r,int x,int v,int p)
{
   int mid=(l+r)>>1;
   if(l==r)
  {
       m[p]=v;
       s[p]=v;
       return;
  }
  if(x<=mid) update(l,mid,x,v,lson);
  else update(mid+1,r,x,v,rson);
  m[p]=max(m[lson],m[rson]);
  s[p]=s[lson]+s[rson];
}

区间修改:

我们可以看出,线段树可以很方便的进行单点修改与区间查询,但区间修改却略显麻烦。

暴力来做,区间修改就是多次单点修改,那么单词修改时间复杂度就退化成了NlogN,十分不理想。

在这里,我们加入lazy数组进行优化

顾名思义,lazy数组可以减少一次区间修改的操作数(就是懒)

原理:

对于区间修改,我们可以不完成这个操作,而是像区间查询一样,把要修改的区间分割成线段树上对应的若干个节点,在这些节点的lazy上加上我们想要的值(这里讲解的是区间加和,覆盖同理)。等到查询的时候,我们再将lazy数组向下推,并且进行赋值之类的操作。

 

int lz[maxn<<2];
int tr[maxn<<2];
int n;
int v[maxn];
#define lson p<<1
#define rson p<<1|1
void mark(int l,int r,int p,int v)
{
   int len=(r-l+1);
   lz[p]+=v;
   tr[p]+=len*v;
}
void pushdown(int l,int r,int p)
{
   int mid=(r+l)>>1;
  mark(l,mid,lson,lz[p]);
  mark(mid+1,r,rson,lz[p]);
  lz[p]=0;
}                                                                                                    
void update_node(int l,int r,int x,int v,int p)
{
   if(l==r){
       tr[p]+=v;
       return ;
  }
   int mid=(l+r)>>1;
   if(lz[p]) pushdown(l,r,p);
   if(x<=mid) update_node(l,mid,x,v,lson);
   else update_node(mid+1,r,x,v,rson);
   tr[p]=tr[lson]+tr[rson];
}
void update(int l,int r,int x,int y,int v,int p)
{
   int mid=(l+r)>>1;
   if(x<=l&&y>=r)
  {
       mark(l,r,p,v);
       return;
  }
   if(lz[p]) pushdown(l,r,p);
   if(x<=mid) update(l,mid,x,y,v,lson);
   if(y>mid) update(mid+1,r,x,y,v,rson);
    tr[p]=tr[lson]+tr[rson];
}
int query(int l,int r,int x,int y,int p)
{
   int mid=(l+r)>>1;
   if(x<=l&&y>=r) return tr[p];
   if(lz[p])
   pushdown(l,r,p);
   int ret=0;
   if(x<=mid) ret+=query(l,mid,x,y,lson);
   if(y>mid) ret+=query(mid+1,r,x,y,rson);
   return ret;
}



浅谈线段树 - 数据结构

标签:bcb   遍历   amp   loaded   log   ima   ade   区间修改   lazy   

原文地址:https://www.cnblogs.com/Marcelo/p/13798886.html

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