• 线段树的收获


    关于线段树的理解,  在自己需要时翻看。

    什么是线段树?

    • 线段树的本质是一棵二叉树,不同于其它二叉树,线段树的每一个节点记录的是一段区间的信息

    线段树的功能?

    • 更新点,查询区间
    • 更新区间,查询点

    • 更新区间,查询区间

    线段树的优势?

    一个长度为N的一维数组(a[1]~a[N])

    我们每次对该数组有一些操作:

    1、修改数组中某个元素的值

    【1,5,4,1,6】---(a[2]=3)---> 【1,3,4,1,6】

    2、询问数组中某段区间的最大值

    【1,5,4,1,6】---(max(1,4)=?)---> 5

    3、询问数组中某段区间的和

    【1,5,4,1,6】---(sum(3,5)=?)---> 11

    如果只有一次询问?

    • 枚举相应区间内的元素,输出答案 O(N)

    更多的询问?

    • Q次询问,O(NQ) That's too SLOW!

    线段树——在O(log2N)的时间内完成每次操作 O(Qlog2N)

    怎么去构造一个线段树?

    • e.g. 对于长度为5的数组a[1]~a[5]
    • 对于任一非叶子节点,若该区间为[L,R],则 左儿子为[L,(L+R)/2] 右儿子为[(L+R)/2+1,R]

    • 我们用一个数组tree记录节点,且根节点的下标为1, 对于任一节点tree[k], 它的左儿子为tree[2*k] 它的右儿子为 tree[2*k+1]

    • 这里我习惯用结构体数组, 感觉比较好理解.(也可以只使用一维数组记录最后叶结点的信息, 只不过在函数多加两个参数L,R即可)
    • struct Tree

    • {    

    •        int left,right; //区间的端点

    •        int max,sum; //视题目要求而定

    • };

    • 如图,就是我们要在线段树中存五个数字所实现的效果。其中, 每个父节点储存了左儿子和右儿子的信息,最后的叶节点储存了数组的信息。

    线段树——代码实现(建树)

     1 void pushup(int id)
     2 {
     3     tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
     4 }
     5 void build(int id, int l, int r)
     6 {
     7     tree[id].left=l;tree[id].right=r;
     8     if(l==r)
     9         tree[id].maxx=tree[id].sum=a[l];
    10     else
    11     {
    12         int mid=(l+r)/2;
    13         build(id<<1, l, mid);
    14         build(id<<1|1, mid+1, r);
    15         pushup(id);
    16     }
    17 }

     线段树——代码实现(查询)

     1 int query(int id, int l, int r)
     2 {
     3     if(l<=tree[id].left && tree[id].right<=r)
     4         return tree[id].sum;
     5     else
     6     {
     7         int ans;
     8         int mid=(tree[id].left+tree[id].right)/2;
     9         if(l<=mid) ans+=query(id<<1, l, r);
    10         if(r>mid) ans+=query(id<<1|1, l, r);
    11         return ans;
    12     }
    13 }

     线段树——代码实现(单点更新)

     1 void update(int id, int pos, int val)
     2 {
     3     if(tree[id].left==tree[id].right)
     4         tree[id].sum=tree[id].maxx=val; 
     5     else
     6     {
     7         int mid=(tree[id].left+tree[id].right)/2;
     8         if(pos<=mid)    update(id<<1, pos, val);
     9         else    update(id<<1|1, pos, val);
    10         pushup(id);
    11     }
    12 }

    下面是上面的完全版代码

     1 #include<iostream>
     2 #include<algorithm>
     3 using namespace std;
     4 const int maxn=1e5+5;
     5 int a[maxn];
     6 struct note
     7 {
     8     int left, right, maxx, sum;
     9 }tree[maxn*4];
    10 void pushup(int id)
    11 {
    12     tree[id].sum=tree[id<<1].sum+tree[id<<1|1].sum;
    13 }
    14 void build(int id, int l, int r)
    15 {
    16     tree[id].left=l;tree[id].right=r;
    17     if(l==r)
    18         tree[id].maxx=tree[id].sum=a[l];
    19     else
    20     {
    21         int mid=(l+r)/2;
    22         build(id<<1, l, mid);
    23         build(id<<1|1, mid+1, r);
    24         pushup(id);
    25     }
    26 }
    27 int query(int id, int l, int r)
    28 {
    29     if(l<=tree[id].left && tree[id].right<=r)
    30         return tree[id].sum;
    31     else
    32     {
    33         int ans;
    34         int mid=(tree[id].left+tree[id].right)/2;
    35         if(l<=mid) ans+=query(id<<1, l, r);
    36         if(r>mid) ans+=query(id<<1|1, l, r);
    37         return ans;
    38     }
    39 }
    40 void update(int id, int pos, int val)
    41 {
    42     if(tree[id].left==tree[id].right)
    43         tree[id].sum=tree[id].maxx=val; 
    44     else
    45     {
    46         int mid=(tree[id].left+tree[id].right)/2;
    47         if(pos<=mid)    update(id<<1, pos, val);
    48         else    update(id<<1|1, pos, val);
    49         pushup(id);
    50     }
    51 }
    52 int main()
    53 {
    54     int n, q, i;
    55     cin>>n>>q;
    56     for(i=1; i<=n; i++)
    57         cin>>a[i];
    58     build(1, 1, n);
    59     while(q--)
    60     {
    61         int op;
    62         cin>>op;
    63         if(op==1)
    64         {
    65             int l, r;
    66             cin>>l>>r;
    67             cout<<query(1, l, r);
    68         }
    69         else
    70         {
    71             int pos, val;
    72             cin>>pos>>val;
    73             update(1, pos, val);
    74         }
    75 
    76     }
    77     
    78 }
    View Code

    然后在贴一个线段树区间更新的代码, 也就是在数组中多加了一个懒标记,很好理解.

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn=1e5+10;
    int a[maxn];
    struct note
    {
        int left,right,maxx,lazy;
        void up(int val)
        {
            maxx+=val;
            lazy+=val;
        }
    } tree[maxn*4];
    void pushup(int id)
    {
        tree[id].maxx=max(tree[id<<1].maxx,tree[id<<1|1].maxx);
    }
    void pushdown(int id)
    {
        if(tree[id].lazy)
        {
            tree[id<<1].up(tree[id].lazy);
            tree[id<<1|1].up(tree[id].lazy);
            tree[id].lazy=0;
        }
    }
    
    void build(int id,int l,int r)
    {
        tree[id].left=l;
        tree[id].right=r;
        if(l==r)
            tree[id].maxx=a[l];
        else
        {
            int mid=(l+r)/2;
            build(id<<1,l,mid);
            build(id<<1|1,mid+1,r);
            pushup(id);
        }
    }
    int query(int id,int l,int r)
    {
        if(l<=tree[id].left&&tree[id].right<=r)
            return tree[id].maxx;
        pushdown(id);
        int mid=(tree[id].left+tree[id].right)/2;
        int ans=-0x3f3f3f3f;
        if(l<=mid) ans=max(ans,query(id<<1,l,r));
        if(r>mid) ans=max(ans,query(id<<1|1,l,r));
        return ans;
    }
    void update(int id,int l,int r,int val)
    {
        if(l<=tree[id].left&&tree[id].right<=r)
        {
            tree[id].up(val);
            return;
        }
        pushdown(id);
        int mid=(tree[id].left+tree[id].right)/2;
        if(l<=mid) update(id<<1,l,r,val);
        if(r>mid) update(id<<1|1,l,r,val);
        pushup(id);
    }
    
    
    int main()
    {
        int n,q;
        scanf("%d%d",&n,&q);
        for(int i=1; i<=n; i++)
            scanf("%d",&a[i]);
        build(1,1,n);
        while(q--)
        {
            int op;
            cin>>op;
            if(op==1)
            {
                int l, r;
                cin>>l>>r;
                cout<<query(1, l, r)<<endl;
            }
            else
            {
                int l, r, val;
                cin>>l>>r>>val;
                update(1, l, r, val);
            }
        }
    }
    View Code
  • 相关阅读:
    程序员有哪些发展方向
    Mysql Join语法解析与性能分析
    jQuery过滤性选择器
    jQuery 基础选择器
    CSS布局模型
    CSS 盒子模型
    CSS 的继承、层叠和特殊性
    java 集合框架(一)
    从客户端中检测到有潜在危险的 Request.Form 值
    线程的一些问题
  • 原文地址:https://www.cnblogs.com/dongdong25800/p/9356048.html
Copyright © 2020-2023  润新知