线段树基础
线段树是一个完全二叉树,在各个节点(1-n)保存一个线段,由于整体是二叉树的构造,可以保证查询的效率。
构建出来的树如上所示,每个结点均对应一段线段。
树的结点信息储存在索引为1-7的数组空间里。
例1:求数组中某区间的最小值
很明显我们要先建树,并且在建树回溯的时候把信息赋给各个结点。
#include<iostream>
using namespace std;
int a[100],segTree[100];
//递归建树
void build(int node,int b,int e){
//到叶子节点直接赋值
if(b == e){
segTree[node] = a[b];
}else{
//继续递归
build(2*node,b,(b+e)/2);
build(2*node+1,(b+e)/2+1,e);
//回溯的时候做一个'归并'的操作,此处比较了node的叶子结点哪个更小,那个小就取哪个赋给node
if(segTree[2*node]<=segTree[2*node+1]){
segTree[node] = segTree[2*node];
}else{
segTree[node] = segTree[2*node+1];
}
}
}
//[l..b...e....r],查询线段树,此处查询node结点的子树中区间在[b,e]的最小值
int query(int node,int b,int e,int l,int r){
int p1,p2;
//不在区间内
if(l>e||r<b){
return -1;
}
//落入区间,直接返回值
if(b>=l&&e<=r){
return segTree[node];
}
//否则说明区间还太大,还需要进一步递归划分
p1 = query(2*node,b,(b+e)/2,l,r);
p2 = query(2*node+1,(b+e)/2+1,e,l,r);
//回溯,如果不在左区间那一定在右区间,不在右区间一定在左区间
if(p1 == -1)
return p2;
if(p2 == -1)
return p1;
//如果左右区间均返回来值需要比较取更小的值
if(p1<=p2)
return p1;
return p2;
}
//更新操作,对某个点做单点的更新
void update(int node,int b,int e,int i,int add){
//找到要更新的点了
if(b == e){
segTree[node] += add;
cout<<"renew : "<<segTree[node]<<endl;
}else{
//继续二分递归
int m = (b+e)/2;
if(i<=m){
update(2*node,b,m,i,add);
}else{
update(2*node+1,m+1,e,i,add);
}
//回溯的时候自下而上更新回去,此处依旧取较小值
segTree[node] = min(segTree[node*2],segTree[node*2+1]);
}
}
int main(){
a[0] = 10;a[1] = 2;a[2] = 2;a[3] = 4;a[4] = 2;a[5] = 1;
//建树,以node = 1结点开始建树,索引范围(0-5)
build(1,0,5);
for(int i=0;i<20;i++){
cout<<segTree[i]<<" ";
}cout<<endl;
//查询node = 1结点的子树,(0-5)源数组区间的最大值,数组索引范围(0-5)
cout<<query(1,0,5,0,5)<<endl;
//更新node = 1结点的子树,(0-5)源数组区间的索引为0的数组元素的值,减11
update(1,0,5,0,-11);
for(int i=0;i<20;i++){
cout<<segTree[i]<<" ";
}cout<<endl;
}