返回顶部

线段树

一:基本操作:

  线段树基于分治思想将区间及其最值进行存储,构成一颗二叉树,每个节点包含区间【L,R】 以及区间的最值,通过递归实现操作。

  由于是二分,线段树是一颗平衡二叉树树高为O(logn)

1:存储

  定义结构体:

1 #define N 100010
2 struct shu{
3     int l,r,mx;
4 }tree[4*N];//主要将区间进行存储

  算法步骤:

    (1)若是叶子节点(l==r),则节点的最值就是对应的a[i]

    (2)非叶子节点,则递归创建左右子树

    (3)节点的区间的最值等于该节点左右子树的最大值

 1 void build(int k,int l,int r){//节点下标从k开始
 2     tree[k].l=l;
 3     tree[k].r=r;
 4     if(l==r){
 5         tree[k].mx=a[l];
 6         return ;
 7     }
 8     int mid=(l+r)/2,lc,rc;
 9     lc=2*k,rc=2*k+1;
10     build(lc,l,mid);
11     build(rc,mid+1,r);
12     tree[k].mx=max(tree[lc].mx,tree[rc].mx);
13 }

2:点更新

  点更新指修改一个元素的值,例如将a[i]修改为v。采用递归进行点更新,算法步骤如下。

    (1)若是叶子节点,满足l=r且l=i,则修改该节点的最值为v。

    (2)若是非叶子节点,则判断是在左子树中更新还是在右子树中更新。

    (3)返回时更新节点的最值。

void update(int k,int i,int v){
    if(tree[k].l==tree[k].r&&tree[k].l==i){//找到a[i]
        tree[k].mx=v;
        return ;
    }
    int mid=(tree[k].l+tree[k].r)/2,lc,rc;
    lc=2*k,rc=2*k+1;
    if(i<=mid)
        update(lc,i,v);
    else
        update(rc,i,v);
    tree[k].mx=max(tree[lc].mx,tree[rc].mx);
}

3:区间查询

  同样采用递归操作

    (1)若节点所在的区间被查询区间[l,r]覆盖,则返回该节点的最值。

    (2)判断是在左子树中查询,还是在右子树中查询。

    (3)返回最值。

 1 int query(int k,int l,int r){
 2     if(tree[k].l>=l&&tree[k].r<=r){
 3         return tree[k].mx;
 4     }
 5     int mid=(tree[k].l+tree[k].r)/2,lc,rc;
 6     lc=2*k,rc=2*k+1;
 7     int maxn=-inf;
 8     if(l<=mid){
 9         maxn=max(query(lc,l,r),maxn);
10     }
11     if(r>mid){
12         maxn=max(query(rc,l,r),maxn);//注意可能会两边同时查询
13     }
14     return maxn;
15 }

总代码:(简易)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100010
 4 #define inf 0x3f3f3f3f
 5 struct shu{
 6     int l,r,mx;
 7 }tree[4*N];
 8 int a[N];
 9 void build(int k,int l,int r){
10     tree[k].l=l;
11     tree[k].r=r;
12     if(l==r){
13         tree[k].mx=a[l];
14         return ;
15     }
16     int mid=(l+r)/2,lc,rc;
17     lc=2*k,rc=2*k+1;
18     build(lc,l,mid);
19     build(rc,mid+1,r);
20     tree[k].mx=max(tree[lc].mx,tree[rc].mx);
21 }
22 void update(int k,int i,int v){
23     if(tree[k].l==tree[k].r&&tree[k].l==i){
24         tree[k].mx=v;
25         return ;
26     }
27     int mid=(tree[k].l+tree[k].r)/2,lc,rc;
28     lc=2*k,rc=2*k+1;
29     if(i<=mid)
30         update(lc,i,v);
31     else
32         update(rc,i,v);
33     tree[k].mx=max(tree[lc].mx,tree[rc].mx);
34 }
35 int query(int k,int l,int r){
36     if(tree[k].l>=l&&tree[k].r<=r){
37         return tree[k].mx;
38     }
39     int mid=(tree[k].l+tree[k].r)/2,lc,rc;
40     lc=2*k,rc=2*k+1;
41     int maxn=-inf;
42     if(l<=mid){
43         maxn=max(query(lc,l,r),maxn);
44     }
45     if(r>mid){
46         maxn=max(query(rc,l,r),maxn);
47     }
48     return maxn;
49 }
50 int main(){
51     int n;
52     cin>>n;
53     for(int i=1;i<=n;i++)
54         scanf("%d",&a[i]);
55     build(1,1,n);
56     int m,v,x;
57     cin>>m;//更新节点,m次操作 
58     for(int i=1;i<=m;i++){ 
59         scanf("%d%d",&x,&v);
60         update(1,x,v);
61     }
62     int a,b,q;//查询a到b的最值 
63     cin>>q;
64     for(int i=1;i<=q;i++){
65         scanf("%d%d",&a,&b);
66         cout<<query(1,a,b)<<endl;; 
67     }
68     return 0;
69 }

二:懒操作

懒操作大概理解就是:到这里就行了,其他的以后遇到再说吧”(可以降低时间复杂度)

1:区间更新 例:将[l,r]区间内的元素都改为v

      (1)若当前节点的区间被查询区间[l,r]覆盖,则仅对该节点进行更新并做懒标记,表示该节点已被更新,对该节点的子节点暂不更新。

    (2)判断是在左子树中查询还是在右子树中查询。在查询过程中,若当前节点带有懒标记,则将懒标记下传给子节点(将当前节点的懒标记清除,将子节点更新并做懒标记),继续查询。

    (3)在返回时更新最值。

 1 struct shu{
 2     int l,r,mx,lz;
 3 }tree[4*N];
 4 void lazy(int k,int  v){
 5     tree[k].mx=v;//更新最值 
 6     tree[k].lz=v;
 7 }
 8 void pushdown(int k){
 9     lazy(2*k,tree[k].lz);
10     lazy(2*k+1,tree[k].lz);
11     tree[k].lz=-1;//注意下传以后要清除 
12 }
13 void update(int k,int l,int r,int v){
14     if(tree[k].l>=l&&tree[k].r<=r)
15         return lazy(k,v);//更新之后返回
16     if(tree[k].lz!=-1)
17         pushdown(k);//因为要更新了,所以必须行动 
18     int mid=(tree[k].l+tree[k].r)/2,lc,rc;
19     lc=2*k,rc=2*k+1;
20     if(l<=mid)
21         update(lc,l,r,v);
22     if(r>mid)
23         update(rc,l,r,v);
24     tree[k].mx=max(tree[lc].mx,tree[rc].mx);
25 }

2:区间查询

  注意查询过程中遇到懒标记就要下传,然后继续查询:

 

 1 int query(int k,int l,int r){
 2     if(tree[k].l>=l&&tree[k].r<=r){
 3         return tree[k].mx;
 4     }
 5     if(tree[k].lz!=-1){
 6         pushdown(k);
 7     }
 8     int mid=(tree[k].l+tree[k].r)/2,lc,rc;
 9     lc=2*k,rc=2*k+1;
10     int maxn=-inf;
11     if(l<=mid){
12         maxn=max(query(lc,l,r),maxn);
13     }
14     if(r>mid){
15         maxn=max(query(rc,l,r),maxn);
16     }
17     return maxn;
18 }

 

分析:

  采用二分分治思想,单点更新,区间更新,区间查询均可在O(logn)时间完成

PS.

posted @ 2021-11-03 10:14  gyc#66ccff  阅读(50)  评论(0编辑  收藏  举报