开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」第13天,点击查看活动详情 上篇文章我们讲解了线段树的建立过程,
我们建立结点时不停往下二分线段,最后到了叶子节点,我们就给它设置原始数据。
PS:绿色数字为步骤数
我们发现叶子节点的,所以不能再建立左右子树了,叶子的pushup也就没用
在结束最后叶子节点的建立后,我们的建树函数就会调用函数,将底层的数据合并到上一层
void pushup(ll root){//例子中的数据为sum(和) 即父节点的sum为子节点的sum合并而来
tree[root].sum=tree[LL(root)].sum+tree[RR(root)].sum;
}
然后该层结点又会把自己数据合并给更上一层
如此这番,我们从最底层叶子结点结束建立后,到根节点结束建立,我们就将每个结点都创建完毕啦。
接着我们来讲下线段树查询答案的步骤
我们可以发现,在建立方面,我们比正常的创建线性数组的效率要低,且空间花费也更多
但是我们正是在查询答案方面效率要比线性查询更优,
查询的思想跟我们建树的思想很像:
倘若当前区间恰好是要查询的区间,那么就返回结果。
如果当前区间包含目标区间:
1.目标区间在中点左侧,就递归查询左区间
2.目标区间在中点右侧,就递归查询右区间
最后返回的总区间答案是由两个子区间答案合并而来
int query(int root,int l,int r)
//当前到了编号为root的节点,查询[l..r]的和{
if(a[root].l==l&&a[root].r==r) return a[root].sum;
//如果当前区间就是询问区间,完全重合,那么显然可以直接返回
int mid=(a[root].l+a[root].r)/2;
if(r<=mid) return query(k*2,l,r);
//如果询问区间包含在左子区间中
if(l>mid) return query(k*2+1,l,r);
//如果询问区间包含在右子区间中
return query(root*2,l,mid)+query(root*2+1,mid+1,r);
//如果询问区间跨越两个子区间
}