前言:
这几天连续没更新是因为小编一直在研究线段树算法,连续做了一个星期,小编终于搞懂了线段树里面的逻辑,线段树算是入门算法里的一个高级算法,刚入门时可能有点难度,但是仔细研究过后,逻辑就会越来越清晰。一下我来分享一下线段树的所有功能实现。
所谓线段树,其实是一种特殊的二叉树,它采用数组的方式表示一种树结构而不是用节点的方式,可能刚入门的小伙伴会疑问,数组和树是两种不同的数据结构,怎么能用数组的方式来表示树结构呢?我们娓娓道来。用数组的方式表示树结构其实就是树的静态存储方式,它受限于数组的大小,需要提前确定好数组的大小,而节点链式存储是树的动态存储,其存储大小不受限,当然静态存储时以牺牲空间来换取时间的方法。如果我们有很多数据保存在一个数组中,而我们要对这个数组进行一些操作,那么使用传统的循环来控制的话,时间复杂度太大,特别是一些算法题很容易超时,这时候线段树可以帮我们解决,将复杂度降为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;
}
运行结果: