线段树是一种二叉搜索树,它可以将一个区间划分为一些单元区间,每个单元区间对应线段树中的一个叶节点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b],所以线段树是平衡二叉树,最后的子节点数目为N,即整个线段树区间的长度。
如图:
其中:节点 i 的权值=她的左儿子权值+她的右儿子权值
建树
设立一个结构体tree,tree[i].l和tree[i],r分别表示这个点的左右下标,tree[i].n表示这个节点的权值和
tree[i].n=tree[i*2].n+tree[i*2+1].n;
建树代码
void build(int t,int l,int r)
{
tree[t].l=l;
tree[t].r=r;
if(l==r)
{ //到达叶子节点
tree[t].n=input[l];
return;
}
int mid=(l+r)/2;
//进入子树开始递归
build(t*2,l,mid);//向左递归
build(t*2+1,mid+1,r);//向右递归
tree[t]=tree[t*2]+tree[t*2+1]; //求和
}
单点修改
通过递归找到更新点,在回溯的时候更新节点值
//单点更新,n为更新值,idx为更新点
void update(int n,int idx,int t)
{
if(tree[t].l=tree[t].r)
{
tree[t].n+=n;
return;
}
int mid=(tree[t].l+tree[t].r)/2;
if(idx<=mid)
update(n,idx,t*2);//在左侧
else
update(n,idx,t*2+1);//在右侧
tree[t].n=tree[t*2].n+tree[t*2+1].n;//回溯过程,需要将线段树上层数据更新
}
区间查询
查询方法:
1、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
2、如果这个区间的左儿子和目标区间有交集,那么搜索左儿子
3、如果这个区间的右儿子和目标区间有交集,那么搜索右儿子
int query(int t,int l,int r)
{
if(tree[t].l>=l&&tree[t].r<=r)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值
return tree[t].n;
if(tree[t].r<l||tree[t].l>r)
return 0;//如果这个区间和目标区间毫不相干,返回0
int sum=0;
if(tree[t*2].r>=l)
sum+=query(t*2,l,r);//如果这个区间的左儿子和目标区间又交集,那么搜索左儿子
if(tree[t*2+1].l<=r)
sum+=query(t*2+1,l,r);//如果这个区间的右儿子和目标区间又交集,那么搜索右儿子
return sum;
}