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

线段树

时间:2018-10-03 22:55:13      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:实现   增加   补齐   turn   amp   tree   pre   span   lock   

动态区间最值问题(查询、更新)
线段树原理简单,但我看刘书上实现代码比较麻烦,于是试着自己实现了一下。说明如下:

  1. 出于简化的目的,总是将线段总长视为2的方幂(如果不足,预先补齐。且这样不会本质上影响复杂度)
  2. 建树:由于是完全二叉树,叶子结点的编号是连续的,建树时只要从底向上扫一遍即可。复杂度\(O(n)\)
  3. 更新:从底向上扫一遍。复杂度\(O(log(n))\)
  4. 查询:按照线段树的思想,找到待查询区间内完全二叉树的树根,并取最小值即可。复杂度的为\(O(log(n))\)

    查询复杂度的证明:线段树的查询复杂度应当时\(O(log(n))\)。由代码可知,可能会使复杂度增加的仅有while循环。但能够进入while,当且仅当此时的l和r在同一颗子树里。分析可知,下一次进入while的树高小于等于上一次出while的树高,因此while循环最多要花\(O(log(n))\)的时间。

int stree[int i]stree[i]表示结点i的值
n补齐后的线段总长
int to_id(int x)原数据编号到结点编号
int query(int l,int r)查询[l,r]区间最值
void update(int p,int v)修改第p个数据为v

const int inf=0x3f3f3f3f;
int stree[4000];
int n;
inline int to_id(int x){return x+n-1;}
int query(int l,int r)
{
    int ans=inf;
    l=to_id(l);r=to_id(r);
    for(int t=l&(-l);l<=r;l=l+t)
    {
        while(l+t-1>r)t/=2;
        ans=min(ans,stree[l/t]);
    }
    return ans;
}
void update(int p,int v)
{
    stree[p=to_id(p)]=v;
    while(p=p/2)stree[p]=min(stree[2*p],stree[2*p+1]);
}

int main()
{
    ...
    int nn;
    while(~scanf("%d",&nn))
    {
        //补齐n
        n=1;
        while(n<nn)n*=2;
        //建树
        for(int i=n;i<2*n;++i)
        {
            int tmp=inf;
            if(i-n<nn)scanf("%d",&tmp);
            stree[i]=tmp;
        }
        for(int i=n-1;i>0;--i)stree[i]=min(stree[2*i],stree[2*i+1]);
        //测试
        int l,r;
        while(~scanf("%d%d",&l,&r))printf("%d\n",query(l,r));
    }
}

线段树

标签:实现   增加   补齐   turn   amp   tree   pre   span   lock   

原文地址:https://www.cnblogs.com/maoruimas/p/9739008.html

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