线段树(Segment Tree)
线段树可在O(log n)的时间复杂度内实现单点修改、区间修改、区间查询(区间和,区间最大/最小值)等操作
线段树的结构和建树
- 线段树建树
- 区间查询(区间求和)
- 单点修改
线段树把一整个线段划分成一个树形结构,将每个长度不为1的区间划分成左右两个区间递归求解。
设用数组st[ ] 来保存线段树, st[i] 为线段树上编号为i的结点的值
(st[i]表示编号为i的结点所管理的区间的和)
st[1] = a[1] + a[2] + a[3] + a[4]
st[2] = a[1] + a[2]
st[4] = a[1]
st[i] 的左儿子结点是st[2*i], 右儿子结点是st[2* i + 1]
假设st[i]管理的区间是[l, r], 则它左儿子管理的区间是[l, (l + r) / 2],它右儿子管理的区间是[(l + r)/2 + 1, r]
继续观察,当区间大小为1的话,那么s[i]管理的区间[l, r]一定有 l==r, 这就是线段树的递归边界
线段树建树
void push_up(int root)
{
st[root] = st[root << 1] + st[root << 1 | 1]; // 左儿子和右儿子管理的区间和
// st[root] = max(st[root << 1] , st[root << 1 | 1]); // 左儿子和右儿子中的最大值
// st[root] = min(st[root << 1] , st[root << 1 | 1]); // 最小值
}
//建树
void build(int root, int l, int r)
{
if(l == r) // 边界
{
cin >> st[root];
return;
}
int mid = l + (r - l) / 2;
build(root << 1, l, mid); // 建root结点的左树
build(root << 1 | 1, mid + 1, r); // 建右树
push_up(root); // 向上更新 区间和
}
区间查询
查询区间[l, r]的和,即查询a[l] + a[l + 1] + a[l + 2] + …… + a[r]
的值(或查询[l,r]的最大/最小值)
见上图,
如要查询区间[1, 4]的和,那么直接取st[1]的值。
如要查询区间[1, 3]的和,此时不能直接获取区间和的值,但是[1, 3]可以拆成区间[1, 2]和区间[3,3],将这两个区间和相加即可。
int query(int root, int