今天做了一些线段树的题,在此简单总结一下。
首先先引入问题:
假设有一个长度为n的数组,对这个数组进行多次如下操作:
1.查询[l,r]区间内的最大/小值,或者是查询[l,r]区间内的元素和;
2.修改数组中某一个元素的值,或者是修改[l,r]区间内的每一个值(都加v)。
如果用朴素方法,那么查询k次的复杂度是O(kn),在k和n都比较大的情况下,这个复杂度远远不能令我们满意,这时线段树这一数据结构应运而生,它的每一次查询和修改操作都可以在logn的复杂度下完成。
闲话不多说,直接讲解一下线段树的几个基本操作。
首先是线段树的结构体:
const int N = 1e5+10;
struct Node{
int left, right;//该节点维护的是[left,right]区间的信息
int sum,max; //sum为[left,right]区间内的元素和,max为最大值
}tree[N<<2];
我的理解是线段树有点类似二叉堆,同样都是满二叉树,可以很方面的保存在数组中,并且用移位运算很便捷的找到父节点或者儿子节点。
1.既然线段树是一种二叉平衡搜索树(BBST),那么首先要建树,代码如下:
void build(int id, int l, int r) //自顶向下递归建树
{
tree[id].left = l;
tree[id].right = r;
if(l == r)
{
scanf("%d", &tree[id].sum); //递归基 ,区间为1的情况,对应的是叶子节点
}
else
{
int mid = (l + r) / 2;
build(id*2, l, mid); //建左子树
build(id*2+1, mid+1, r); //建右子树
tree[id].sum = tree[id*2].sum + tree[id*2+1].sum;
}
}2.接下来的操作是修改某个叶子节点的值:
void update(int id, int pos, int val)
{
if(tree[id].left == tree[id].right)
tree[id].sum += val; //将叶子节点的值加val
// tree[id].sum = val; //将叶子节点的值修改为val
else
{
int mid = (tree[id].left + tree[id].right) / 2;
if(pos <= mid)
update(id*2, pos, val);
else
update(id*2+1, pos, val);
tree[id].sum = tree[id*2].sum + tree[id*2+1].sum;
}
}3.最后一个操作是查询某个区间:
int query(int id, int l, int r)
{
if(tree[id].left == l && tree[id].right == r)
return tree[id].sum;
else
{
int mid = (tree[id].left + tree[id].right) / 2;
if(r <= mid)
return query(id*2, l, r);
else if(l > mid)
return query(id*2+1, l, r);
else
return query(id*2, l, mid) + query(id*2+1, mid+1, r);
}
}
2382

被折叠的 条评论
为什么被折叠?



