线段树是在处理区间和问题和在线更新问题中取得一个折中办法
当我们遇到一个问题的时候:
使用前缀和:区间查询o(1),更新值o(n)
使用数组: 区间查询o(n),更新值o(1)
而线段树通过树的性质,把区间查询和更新值都压到了o(logn)
具体原理如下:
1.对于【1,5】的数组,可以形成这样一个树
2.区间查询操作
【2,4】 可以由【2】+【3,4】组成,每个组成所求区间的小区间查询的复杂度最坏为o(logn),由此达到压缩时间复杂度的目的
3.更新值操作
改变【2】的时候,需要改变【2】,【1,2】,【1,5】,也就是说,最多进行o(logn)次操作,时间复杂度为o(logn)
有个问题就是过程中使用>>1比使用/2要快一点,所以写的时候可以写成>>1,不然可能会卡常,就很难受 node/2+1可以表示 node>>1|1
还有就是tree数组只要比arr数组开大四倍就行了。。。
具体代码见最后
我感觉有很多题目到最后都可以转化成在一个区间上的问题,然后使用一些奇奇怪怪的区间方法来算出来结果
一般涉及区间问题的算法有:
前缀和: o(1)对区间查询
差分数组: 前缀和的逆运用
线段树
双指针: o(n)的复杂度寻找一个区间
滑窗: 感觉就是一个特殊的双指针
莫队: 一种离线处理算法,当查询的次数非常多的时候适宜使用,利用分块算法对多次的查 询进行排序,然后通过左右两个指针一个一个挪过去同时维护区间值的方法从一个区间转移到另一个区间
建树的复杂度o(n),单点更新和区间查询的复杂度o(logn)
在部分题目中,由于非零的值比较少,可以尝试不用建树,直接update即可
(第一段代码比较好理解, 第二段代码是优化后)
void built_tree(int node,int st,int ed)
{
if(st==ed) {tree[node]=arr[st];return ;}
int mid=(st+ed)/2;
int left_node =node*2+1;
int right_node=node*2+2;
built_tree(left_node,st,mid);
built_tree(right_node,mid+1,ed);
tree[node]=tree[left_node]+tree[right_node];
}
void update_tree(int node,int st,int ed,int idx,int val)
{
if(st==ed){
arr[idx]=val;
tree[node]=val;
return ;
}
int mid=(st+ed)/2;
int left_node =node*2+1;
int right_node=node*2+2;
if(idx>=st&&idx<=mid)
update_tree(left_node,st,mid,idx,val);
else
update_tree(right_node,mid+1,ed,idx,val);
tree[node]=tree[left_node]+tree[right_node];
}
int query_tree(int node,int st,int ed,int ll,int rr)
{
if(rr<st||ll>ed) return 0;
else if(st==ed) return tree[node];
else if(ll<=st&&ed<=r) return tree[node];
int mid=(st+ed)/2;
int left_node =node*2+1;
int right_node=node*2+2;
int sum_left=query_tree(left_node,st,mid,ll,rr);
int sum_right=query_tree(right_node,mid+1,ed,ll,rr);
return sum_left+sum_right;
}
优化后
void update_tree(int node,int st,int ed,int idx,int val)
{
if(st==ed){
tree[node]+=val;
return ;
}
int mid=(st+ed)>>1;
if(idx<=mid) update_tree(node<<1,st,mid,idx,val);
else update_tree(node<<1|1,mid+1,ed,idx,val);
tree[node]=tree[node<<1]+tree[node<<1|1];
}
int query_tree(int node,int st,int ed,int ll,int rr)
{
if(ll>rr) return 0;
else if(ll<=st&&rr>=ed) return tree[node];
//else if(ll<=st&&ed<=rr) return tree[node];
int mid=(st+ed)>>1;
int ans=0;
if(ll<=mid) ans+=query_tree(node<<1,st,mid,ll,rr);
if(rr>mid) ans+=query_tree(node<<1|1,mid+1,ed,ll,rr);
return ans;
}
```