线段树学习笔记

来源:互联网 发布:华为机顶盒 mac地址 编辑:程序博客网 时间:2024/05/16 12:05

线段树,就是一棵由线段构成的二叉树,每个节点代表一条线段[a,b],非叶子的结点所对应的线段都有两个子结点,左儿子代表的线段为[a,a+b/2],右儿子代表的线段为[a+b/2+1,b]类似二分的思想;

线段树可以查找并修改连续区间内的信息,优化区间操作;

如:对序列A1,A2,A3……An,操作 1  x v表示给Ax+v

操作 2  x y查询,即询问[Ax,Ay]的区间和;


当x=5 n=8
根节点为[1,8]
sum[1,8]+=v
其两个子节点为[1,4]  [5,8]
因为x∈[5,8] 我们选择右儿子
sum[5,8]+=v
以此类推,直到当前结点为[5,5]
中间选择的每个子节点的sum都要加上v
 
长度范围为[1,n]的一棵线段树的深度为log(n)+1.
单次修改区间数量是log级别,复杂度O(logn)
void change(int   p,int left,int right,int x,int v){        sum[p]+=v;        int t=(left + right)/2;        if(left==right)               return;//如果已经是叶子        if(x<=t)//如果左儿子               change(p*2,left,t,x,v);        else        change(p*2+1,t+1,right,x,v);}      

注意,对于一个节点p,其左子节点、右子节点的编号分别为2*p,2*p+1;

而对于查询,当x=5,y=7,n=8;我们需要求出[A5,A7]的信息

[A5,A7]又等于[5,6],[7,7]的并,由于线段树每层的结点最多只会被选取两个,所以查询是O(logn)

int  ask(int p,int left,int right, int x,int y){      if(x<=left&&right<=y)//在查询区间内            return sum[p];      int t=(left+right)/2,res=0;      if(x<=t)  res+=ask(p*2,left,t,x,y);         if(y>t) res+=ask(p*2+1,t+1,right,x,y);      return res;}