线段树(C/C++)

前言:

这几天连续没更新是因为小编一直在研究线段树算法,连续做了一个星期,小编终于搞懂了线段树里面的逻辑,线段树算是入门算法里的一个高级算法,刚入门时可能有点难度,但是仔细研究过后,逻辑就会越来越清晰。一下我来分享一下线段树的所有功能实现。

所谓线段树,其实是一种特殊的二叉树,它采用数组的方式表示一种树结构而不是用节点的方式,可能刚入门的小伙伴会疑问,数组和树是两种不同的数据结构,怎么能用数组的方式来表示树结构呢?我们娓娓道来。用数组的方式表示树结构其实就是树的静态存储方式,它受限于数组的大小,需要提前确定好数组的大小,而节点链式存储是树的动态存储,其存储大小不受限,当然静态存储时以牺牲空间来换取时间的方法。如果我们有很多数据保存在一个数组中,而我们要对这个数组进行一些操作,那么使用传统的循环来控制的话,时间复杂度太大,特别是一些算法题很容易超时,这时候线段树可以帮我们解决,将复杂度降为nlog(n),在此说明:线段树的应用通常是一些区间求和,区间修改,区间最大值、最小值等操作。

1.我们首先要做的是创建线段树,采用递归的方式来创建。

代码如下:

首先是递归方便递归树的左右儿子:

inline ll ls(int p){
    return p<<1;
}
inline ll rs(int p){
    return p<<1|1;
}
inline void buildtree(ll k, ll start, ll end){
    if(start == end){
        tree[k] = arr[start];
        return;
    };
    ll mid = (start + end) >> 1;
    buildtree(ls(k),start,mid);
    buildtree(rs(k),mid+1,end);
    tree[k] = tree[ls(k)] + tree[rs(k)];    /*回溯修改父节点*/
}

特别注意一定要回溯修改父节点,因为父节点存储的是区间的信息,比如和或者积等。

接着我们要实现更新线段树的函数,这里提供两种修改方法,一种是单点修改,一种是区间修改,区间修改引入lazy标记以便快速求值。

单点修改:

/*单点修改*/
inline void update(ll p,ll val, ll k, ll start, ll end){
    ll mid = (start + end) >> 1;
    if(start == end){
        if(p == start) {
            tree[k] = val;
            return;
        }
    }
    if(p <= mid) update(p,val,ls(k),start,mid);
    if(p > mid) update(p,val,rs(k),mid+1,end);
    tree[k] = tree[ls(k)] + tree[rs(k)];
}

区间修改:

进行区间修改时,采用lazy标记,传递修改信息。

/*lazy标记*/
inline void lazy_push_down(ll k , ll start, ll end){
    if(tag[k] != 0){
        tree[k] += (end - start + 1)*tag[k];
        if(start != end){
            tag[ls(k)] += tag[k];
            tag[rs(k)] += tag[k];
        }
        tag[k] = 0;
    }
}

区间修改:

/*区间修改*/
inline void update(ll nl, ll nr, ll val, ll k, ll start, ll end){
    lazy_push_down(k,start,end);
    if(nl <= start && nr >= end){
        tree[k] += (end - start + 1)*val;
        if(start != end){
            tag[ls(k)] += val;
            tag[rs(k)] += val;
            lazy_push_down(k,start,end);
        }
        return;
    }
    if(nl > end || nr < start) return;
    ll mid = (end + start) >> 1;
    update(nl,nr,val,ls(k),start,mid);
    update(nl,nr,val,rs(k),mid+1,end);
    tree[k] = tree[ls(k)] + tree[rs(k)];
}

最后就是查询函数:

inline ll query(ll nl, ll nr,ll k , ll start, ll end){
    lazy_push_down(k,start,end);
    ll leftsum = 0, rightsum = 0;
    if(nl <= start && nr >=end) return tree[k];
    // if(nl > end || nr < start) return 0;
    if(start == end) return tree[k];
    ll mid = (end + start) >> 1;
    if(nl <= mid)  leftsum = query(nl,nr,ls(k),start,mid);
    if(nr > mid)   rightsum = query(nl,nr,rs(k),mid+1,end);
    return leftsum + rightsum;
}

全部代码:

#include<bits/stdc++.h>
using namespace std;

typedef  long long ll;

vector<ll> arr;
vector<ll> tree;
vector<ll> tag;

inline ll ls(int p){
    return p<<1;
}
inline ll rs(int p){
    return p<<1|1;
}

inline void buildtree(ll k, ll start, ll end){
    if(start == end){
        tree[k] = arr[start];
        return;
    };
    ll mid = (start + end) >> 1;
    buildtree(ls(k),start,mid);
    buildtree(rs(k),mid+1,end);
    tree[k] = tree[ls(k)] + tree[rs(k)];    /*回溯修改父节点*/
}

/*lazy标记*/
inline void lazy_push_down(ll k , ll start, ll end){
    if(tag[k] != 0){
        tree[k] += (end - start + 1)*tag[k];
        if(start != end){
            tag[ls(k)] += tag[k];
            tag[rs(k)] += tag[k];
        }
        tag[k] = 0;
    }
}

/*区间修改*/
inline void update(ll nl, ll nr, ll val, ll k, ll start, ll end){
    lazy_push_down(k,start,end);
    if(nl <= start && nr >= end){
        tree[k] += (end - start + 1)*val;
        if(start != end){
            tag[ls(k)] += val;
            tag[rs(k)] += val;
            lazy_push_down(k,start,end);
        }
        return;
    }
    if(nl > end || nr < start) return;
    ll mid = (end + start) >> 1;
    update(nl,nr,val,ls(k),start,mid);
    update(nl,nr,val,rs(k),mid+1,end);
    tree[k] = tree[ls(k)] + tree[rs(k)];
}

/*单点修改*/
inline void update(ll p,ll val, ll k, ll start, ll end){
    ll mid = (start + end) >> 1;
    if(start == end){
        if(p == start) {
            tree[k] = val;
            return;
        }
    }
    if(p <= mid) update(p,val,ls(k),start,mid);
    if(p > mid) update(p,val,rs(k),mid+1,end);
    tree[k] = tree[ls(k)] + tree[rs(k)];
}

inline ll query(ll nl, ll nr,ll k , ll start, ll end){
    lazy_push_down(k,start,end);
    ll leftsum = 0, rightsum = 0;
    if(nl <= start && nr >=end) return tree[k];
    // if(nl > end || nr < start) return 0;
    if(start == end) return tree[k];
    ll mid = (end + start) >> 1;
    if(nl <= mid)  leftsum = query(nl,nr,ls(k),start,mid);
    if(nr > mid)   rightsum = query(nl,nr,rs(k),mid+1,end);
    return leftsum + rightsum;
}

int main(){
    ll n ,m;
    // scanf("%lld%lld",&n,&m);
    cin >> n >> m;
    arr.resize(n+1,0), tree.resize(n<<2,0), tag.resize(n<<2,0);
    for(ll i = 1; i <= n ; ++i){
        cin >> arr[i];
    }
    buildtree(1,1,n);
    while(m--){
        ll f,a,b;
        // scanf("%d%d%d%d",&f,&a,&b,&c);
        cin >> f >> a >> b ;
        if(f == 1) {
            ll c;
            cin >> c;
            update(a,b,c,1,1,n);    /*区间修改*/
            // update(a,b,1,1,n);   /*索引修改*/
            }
        else
            cout << query(a,b,1,1,n) << endl;
       
    }
    return 0;
}

运行结果:

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值