线段树是一种 二叉树存储结构
如果要求出一个时常更新的序列的一个连续子序列和/极值,我们可以用二叉树。
like?
一座大楼有n个停车场,加入你要做调查,统计从l-r号停车场每个时段的车辆数,只要用lg(N)个时间代价就可以做到实时更新(N为车辆数)。
具体实现可以参看以下博文:
线段树(segment tree),看这一篇就够了
线段树的结构可以这样解释:
单个数组元素生成底层叶子,一个根节点管着两个子节点,根的值根据需要修改(可以是和、极值等),然后向上迭代,这样上面的根所包含的区间就是由子树组成的序列闭区间[l,r]
更新操作 类似于堆调整的处理方法,从叶子顺着到根,进行数据的更新,花费代价为O(lg(N))
一般空间大小开原数据的2-4倍哦~
一般有数组和自定义结点两种写法。
简述:
敌方有n个兵营,你作为侦查员要实时监测兵营人数,对几个连续的兵营人数求和
总共有Add/Sub/Query增删查(区间)三种操作
本题采用数组
附上关键代码,以及自己的一些注释
buildTree
//建树,只需要两个点,一个起点,一个终点
if(left == right)
{
//输入兵营里的人数
scanf("%d", &segment[root]);
return;
}
int mid = (left + right) / 2;
buildTree(root * 2, left, mid);//建左树
buildTree(root * 2 + 1, mid + 1, right);//建右数
//调整它的上面节点的值
pushUp(root);//**注意这里的顺序,子节点的值有了,才能对根赋值
update
注意pushUp的位置
if (left == right)
{
segment[root] += add_num;
return;//**记得要return
}
int mid = (left + right) / 2;
if (pos <= mid)
update(root * 2, pos, add_num, left, mid);//更新节点在左区间
else
update(root * 2 + 1, pos, add_num, mid + 1, right);//更新节点在左区间
//向上调整
pushUp(root);
getSum
注意三个分支
第三个分支需要再分为两个区间(即两个子树)内进行求和
//如果在当前节点的右半个区间内
if(left > mid)
{
res += getSum(root * 2 + 1, left, right, mid + 1, R);
}
//如果在当前查询区间段的左半个区间内
else if(right <= mid)
{
res += getSum(root * 2, left, right, L, mid);
}
//一个在左半边,一个在右半边
else
{
res += getSum(root * 2, left, mid, L, mid);//**注意理解这两句话
res += getSum(root * 2 + 1, mid + 1, right, mid + 1, R);//这里 right 和 R 的位置是对称的
}
第二种结点数据结构形式…