您的位置:首页 > 编程语言 > C语言/C++

线段树选讲

2015-10-16 20:46 453 查看
线段树选讲

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。






第一初始化:

int tree[maxn];
void buildtree(int left,int right,int rt)
{
if(left==right)           //计算到根节点
{
scanf("%d",&tree[rt]);
return;
}
int mid=(left+right)/2;
buildtree(left,mid,rt<<1);        //左子区间
buildtree(mid+1,right,rt<<1|1);   //右子区间
pushup(rt);                       //每个区间的值由左右区间的值共同决定(由叶向根计算)
}


第二更新:

单点更新:

单点更新只要更新叶子节点,所以除了改变这个点的值还需要从叶子节点向上更新区间数值

void update(int left,int right,int pos,int change,int rt)
{
if(left==right)
{
tree[rt]=change;              //这里以替换距离,其他操作也可以
return;
}
int mid=(left+right)/2;
if(mid>=pos)                      //如果要更新的点在左子区间
update(left,mid,pos,change,rt<<1);
else                              //如果要更新的点在右子区间
update(mid+1,right,change,rt<<1|1);
pushup(rt);                        //向上修正区间值到根节点
}


        区间更新:

区间更新和单点更新不同,区间更新时除了改变该段区间的值并向上传递之外,还需要将其所有子区间更新,

void update(int L,int R,int left,int right,int rt,int change)     //L,R为要求更新区间
{
if(left>R||right<L)                                           //这种明显不在区间内的情况就直接跳出
return;
if(L<=left&&right<=R)
{
tree[rt]+=change*(right-left+1);                           //这里以区间增加为例子
add[rt]=change;
return;
}
pushdown(left,right,rt);                                                   //向下更新又叫延迟操作
int mid=(left+right)/2;
if(mid>=L)
update(L,R,left,mid,rt<<1,change);
if(mid+1<=R)
update(L,R,mid+1,right,rt<<1|1,change);
pushup(rt);                                                     //和单点更新一样向根节点更新区间值
}

        这里比单点更新多了一个pushdown(),这个函数是在之后的子区间中改变他们对应的值而准备的。

void pushdown(int left,int right,int rt)
{
if(add[rt])
{
add[rt<<1]=add[rt<<1|1]=add[rt];     //继续向下更新
int mid=(left+right)/2;
tree[rt<<1]+=add[rt]*(mid-left+1);
tree[rt<<1|1]+=add[rt]*(right-mid);
add[rt]=0;                           //清除更新
}
}

第三询问:

单点询问:

int query(int left,int right,int rt,int pos)                       //询问pos点的值
{
if(left==right)                                                //找到叶节点
return tree[rt];
int mid=(left+right)/2;
if(mid<=pos)
query(left,mid,rt<<1,pos);
else
query(mid+1,right,rt<<1|1,pos);
pushup(rt);                                                     //其实我感觉不需要
}
        区间询问:

int query(int L,int R,int left,int right,int rt)
{
if(left>R||right<L)                                           //这种明显不在区间内的情况就直接跳出
return 0;
if(L<=left&&right<=R)
return tree[rt];
pushdown(left,right,rt);
int ans=0;                                                    //区间的值等于左右子区间的和
int mid=(left+right)/2;
if(mid>=L)
ans+=query(L,R,left,mid,rt<<1);
if(mid+1<=R)
ans+=query(L,R,mid+1,right,rt<<1|1);
pushup(rt);
return ans;
}


综上线段树的三个功能讲完!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息