线段树详解

索引(Index)

在求一段区间的值,比如说求区间最大值,或要在某一段区间修改他们的值,那么我们就可以使用线段树(segment tree)实现。这种数据结构在大型OI比赛中常考,所以说掌握了线段树就可以在OI比赛中得到某题的高分。同时,线段树在日常生活中很实用,是一种算法的基础。

介绍(Introduction)

线段树,英文segment tree,顾名思义,它是一棵树,是一棵满二叉树。树上的每一个节点都代表着一段区间。线段树数据结构思想:二分思想(binary search)。首先根节点是整个区间,然后每一个节点的左儿子代表父节点区间的左半区间,右儿子代表父节点区间的右半区间,如下图。
线段树详解_第1张图片
我们可以用这棵树维护区间的很多东西,如最大值,区间和等。
时间复杂度: O(logn)

操作(Operations)

建造线段树(Build a segment tree)

方法:递归(recursion)。
维护两个指针l,r,如果l=r,那么就返回需要的值;否则将区间[l,r]分成两半继续递归。

伪代码

void build(线段树下标,指针)
{
    如果区间大小为1(即l,r指针相同) 记录值
    build(leftson);
    build(rightson);
    tree[下标]=max(tree[leftson],tree[rightson]);//假设现在要求的是区间最大值
}

代码(Code)

void build(int x,int l,int r)//假设现在要求的是区间最大值
{
    if (l==r)
    {
        tree[x]=a[l];
        return;
    }
    int wz=(l+r)/2;
    build(x*2,l,wz);
    build(x*2+1,wz+1,r);
    tree[x]=max(tree[x*2],tree[x*2+1]);
}

修改操作(Modify)

递归下去,如果发现指针一样,那么就修改,否则将区间分成两份,继续递归。

代码(Code)

void modify(int x,int l,int r,int xb,int val)
{
    if (l==r)
    {
        tree[x]=val;
        return;
    }
    int wz=(l+r)/2;
    if (xb<=wz) modify(x*2,l,wz,xb,val);
           else modify(x*2+1,wz+1,r,xb,val);
    tree[x]=max(tree[x*2],tree[x*2+1]);
}

查找操作(Find)

跟上面一样,找到了就退,没找到就将区间分成两份,继续递归。

代码(Code)

int find(int x,int l,int r,int xb)
{
    if (l==r)
    {
        return max(find,tree[x]);
    }
    int wz=(l+r)/2;
    if (xb<=wz) find(x*2,l,wz,xb);
           else find(x*2+1,wz+1,r,xb);
}

难点(Special difficulties)

如果修改的不是一个位置,而是一段区间,那又怎么办?
我们回去看到上面介绍线段树的那幅图,如果要求2-3区间的最大值,怎么办?无非就是求2-2和3-3的最大值吗?是的。
怎么实现不用暴力修改区间?
没事!再让tree数组开多一维,tree[1]记录值,tree[2]记录修改的数,这样防止在搜索更小的区间时出现错误,从而实现不用暴力修改区间。

实现方法(The way to achieve)

区间修改:在二分区间的时候顺便将要修改的区间也拆分掉(具体见下面的代码)。
Lazy标志:在修改的时候将tree[1]和tree[2]修改为要修改的数,然后后面递归的时候将tree[2]的值记录到tree[leftson][2],tree[rightson][2]里面,实现不用暴力修改区间。

代码(Code)

void modify(int x,int l,int r,int l1,int r1,int val)
{
    if (l==r)
    {
        tree[x][1]=tree[x][2]=val;
        return;
    }
    if (tree[s][2]!=-1)
    {
        tree[s*2][1]=tree[s][2];
        tree[s*2][2]=tree[s][2];
        tree[s*2+1][1]=tree[s][2];
        tree[s*2+1][2]=tree[s][2];
        tree[s][2]=-1;  //此时表示大区间修改完了。
    }
    int wz=(l+r)/2;
    if (r1<=wz) modify(x*2,l,wz,l1,r1,val);
        else if (l1>wz) modify(x*2+1,wz+1,r,l1,r1,val);
             else
             {
                  modify(x*2,l,wz,l1,wz,val);
                  modify(x*2+1,wz+1,r,wz+1,r1,val);
             }
    tree[x][1]=max(tree[x*2][1],tree[x*2+1][1]);
}

总结(Summaries)

如果题目问到要求区间或是什么的,就要想到线段树,线段树的代码其实很好打,不要因为怕长就放弃,想暴力。如果理解了,就马上学会了。

你可能感兴趣的:(初级模板库及巧径)