线段树是树状数组的变种,用途是在log(n)内查询一个指定区间的咨询(sum、max、min……),树状数组能实现的功能线段树全可以实现,线段树可实现的功能树状数组不一定可以实现。
线段树也是一种二叉搜索树,每个结点存储了一个区间。
线段树的存储利用数组存储,类似于堆
以求区间和为例,线段树做法如下
#include <bits/stdc++.h>
using namespace std;
const int N=10010;
int a[N] //原数组
struct Node //线段树结点
{
int l, r; //区间左端点,右端点
int sum; //区间和
}tr[N * 4]; //开4倍的N的空间
void pushup(int u) //根据两个子节点sum求父节点sum
{
tr[u].sum = tr[u * 2].sum + tr[u * 2 + 1].sum;
}
void build(int u, int l, int r) //建立线段树
{
tr[u].l=l;
tr[u].r=r;
if (l == r) tr[u].sum=a[r];
else
{
int mid = ( l + r ) / 2;
build(u * 2, l, mid); //建立左子树
build(u * 2 + 1, mid + 1, r); //建立右子树
pushup(u); //更新此节点
}
}
int query(int u, int l, int r) //求区间[l,r]的和
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum; //如果tr[u]介于[l,r]之间
int mid = ( tr[u].l + tr[u].r )/2 ;
int sum = 0;
if (l <= mid) sum = query(u * 2, l, r); //如果[l,r]包含tr[u]的左子树的部分
if (r > mid) sum += query(u * 2 + 1, l, r); //如果[l,r]包含tr[u]的右子树的部分
return sum;
}
void modify(int u, int x, int v) //修改线段树
{
if (tr[u].l == tr[u].r) tr[u].sum += v; //找到叶子结点
else
{
int mid = ( tr[u].l + tr[u].r ) / 2;
if (x <= mid) modify(u * 2, x, v); //如果目标叶子结点在左子树
else modify(u * 2 + 1, x, v); //目标叶子结点在右子树
pushup(u); //更新此结点的sum
}
}